using ERPCore.Domain.Entities; using ERPCore.Dtos.Customers; using ERPCore.Infra.Persistence; using ERPCore.Repositories; using ERPCore.Services; using ERPCore.System.Errors; using ERPCore.Tests.TestSupport; using static ERPCore.Tests.TestSupport.BankDepositTestSupport; namespace ERPCore.Tests; /// /// Sales Configuration decides which optional customer fields exist (phone, email, address, city, country, credit limit, /// credit days). The forms hide a switched-off field; the server refuses a new value for it and keeps what is stored. /// public class CustomerFieldSwitchTests { private static CustomerService Svc(ErpDbContext db) => new(new Repository(db), new Repository(db), new FakeBranchConfig(), new Repository(db), new Repository(db), new Repository(db), new PassThroughUnitOfWork(db)); private static async Task SwitchesAsync(ErpDbContext db, Action tweak) { var config = new SalesConfig { ConfigId = SalesConfig.SingletonId }; tweak(config); db.SalesConfig.Add(config); await db.SaveChangesAsync(); } private static CreateCustomerRequest NewCustomer(Action? tweak = null) { var request = new CreateCustomerRequest { CustomerCode = "C-1", Name = "Nimal Perera", BranchId = Branch1 }; tweak?.Invoke(request); return request; } private static UpdateCustomerRequest Same(Customer c, Action? tweak = null) { var request = new UpdateCustomerRequest { CustomerCode = c.CustomerCode, CustomerType = c.CustomerType, Name = c.Name, DisplayName = c.DisplayName, Phone = c.Phone, Email = c.Email, AddressLine1 = c.AddressLine1, AddressLine2 = c.AddressLine2, City = c.City, Country = c.Country, TaxRegistrationNo = c.TaxRegistrationNo, CreditLimit = c.CreditLimit, CreditDays = c.CreditDays, BranchId = c.BranchId }; tweak?.Invoke(request); return request; } [Fact] public async Task EveryFieldIsOn_WhenNothingIsConfigured_SoExistingBehaviourIsUnchanged() { await using var db = CreateContext(); // no sales_config row at all var created = (await Svc(db).CreateAsync(NewCustomer(r => { r.Phone = "0771234567"; r.Email = "a@b.lk"; r.AddressLine1 = "No. 12"; r.City = "Colombo"; r.Country = "Sri Lanka"; r.CreditLimit = 5000m; r.CreditDays = 30; }))).Value; Assert.Equal(("0771234567", "a@b.lk", "No. 12", "Colombo", "Sri Lanka"), (created.Phone, created.Email, created.AddressLine1, created.City, created.Country)); Assert.Equal((5000m, 30), (created.CreditLimit, created.CreditDays)); } [Theory] [InlineData("phone")] [InlineData("email")] [InlineData("address")] [InlineData("city")] [InlineData("country")] [InlineData("creditLimit")] [InlineData("creditDays")] public async Task Create_ANewValueForASwitchedOffField_IsRefused(string field) { await using var db = CreateContext(); await SwitchesAsync(db, c => { c.CustomerPhoneEnabled = field != "phone"; c.CustomerEmailEnabled = field != "email"; c.CustomerAddressEnabled = field != "address"; c.CustomerCityEnabled = field != "city"; c.CustomerCountryEnabled = field != "country"; c.CustomerCreditLimitEnabled = field != "creditLimit"; c.CustomerCreditDaysEnabled = field != "creditDays"; }); var ex = await Assert.ThrowsAsync(() => Svc(db).CreateAsync(NewCustomer(r => { switch (field) { case "phone": r.Phone = "0771234567"; break; case "email": r.Email = "a@b.lk"; break; case "address": r.AddressLine2 = "Wellawatte"; break; case "city": r.City = "Colombo"; break; case "country": r.Country = "Sri Lanka"; break; case "creditLimit": r.CreditLimit = 100m; break; default: r.CreditDays = 7; break; } }))); Assert.Equal(ErrorCodes.ConfigDisabled, ex.Code); Assert.Equal(422, ex.StatusCode); Assert.Empty(db.Customers.Where(c => c.CustomerCode == "C-1")); } [Fact] public async Task Create_WithTheSwitchedOffFieldsLeftBlank_Works() { await using var db = CreateContext(); await SwitchesAsync(db, c => { c.CustomerPhoneEnabled = c.CustomerEmailEnabled = c.CustomerAddressEnabled = false; c.CustomerCityEnabled = c.CustomerCountryEnabled = false; c.CustomerCreditLimitEnabled = c.CustomerCreditDaysEnabled = false; }); var created = (await Svc(db).CreateAsync(NewCustomer())).Value; Assert.Equal("Nimal Perera", created.Name); Assert.Null(created.Phone); Assert.Equal((0m, 0), (created.CreditLimit, created.CreditDays)); } [Fact] public async Task Update_KeepsAStoredValueWhoseFieldWasSwitchedOff_EvenWhenTheFormSendsItBlank() { await using var db = CreateContext(); var svc = Svc(db); var created = (await svc.CreateAsync(NewCustomer(r => { r.Phone = "0771234567"; r.CreditLimit = 5000m; r.CreditDays = 30; }))).Value; await SwitchesAsync(db, c => { c.CustomerPhoneEnabled = false; c.CustomerCreditLimitEnabled = false; c.CustomerCreditDaysEnabled = false; }); var stored = db.Customers.Single(c => c.CustomerId == created.CustomerId); // The edit form no longer has the fields, so it sends blanks / zeros. var updated = (await svc.UpdateAsync(created.CustomerId, Same(stored, r => { r.Name = "Nimal P."; r.Phone = null; r.CreditLimit = 0m; r.CreditDays = 0; }), stored.RowVersion)).Value; Assert.Equal("Nimal P.", updated.Name); Assert.Equal("0771234567", updated.Phone); Assert.Equal((5000m, 30), (updated.CreditLimit, updated.CreditDays)); } [Fact] public async Task Update_ADifferentValueForASwitchedOffField_IsRefused() { await using var db = CreateContext(); var svc = Svc(db); var created = (await svc.CreateAsync(NewCustomer(r => r.CreditLimit = 5000m))).Value; await SwitchesAsync(db, c => c.CustomerCreditLimitEnabled = false); var stored = db.Customers.Single(c => c.CustomerId == created.CustomerId); var ex = await Assert.ThrowsAsync( () => svc.UpdateAsync(created.CustomerId, Same(stored, r => r.CreditLimit = 9999m), stored.RowVersion)); Assert.Equal(ErrorCodes.ConfigDisabled, ex.Code); } }