using ERPCore.Domain.Entities; using ERPCore.Domain.Enums; using static ERPCore.Tests.TestSupport.BankDepositTestSupport; namespace ERPCore.Tests; /// Till cash in hand (docs/BANK_DEPOSIT_MODULE_PLAN.md §2, §11). public class CashInHandServiceTests { private static readonly DateOnly Today = Clock.Today; private static readonly DateOnly Start = Today.AddDays(-10); [Fact] public async Task OnlyCashIsCounted() { await using var db = CreateContext(); var day = Today.AddDays(-2); db.SalesSlips.AddRange( CashSlip(day, cash: 300, card: 200), // only the 300 is till cash CashSlip(day, cash: 0, card: 900)); db.SalesInvoices.Add(new SalesInvoice { SalesInvoiceId = 1, InvoiceNo = "INV-1", InvoiceDate = At(day.AddDays(-5)), Status = SalesInvoiceStatus.Posted, BranchId = Branch1 }); db.SalesInvoicePayments.AddRange( new SalesInvoicePayment { SalesInvoicePaymentId = 1, SalesInvoiceId = 1, PaymentDate = At(day), PaymentMethod = SalesPaymentMethod.Cash, Amount = 150 }, new SalesInvoicePayment { SalesInvoicePaymentId = 2, SalesInvoiceId = 1, PaymentDate = At(day), PaymentMethod = SalesPaymentMethod.Card, Amount = 500 }, new SalesInvoicePayment { SalesInvoicePaymentId = 3, SalesInvoiceId = 1, PaymentDate = At(day), PaymentMethod = SalesPaymentMethod.Cheque, Amount = 70 }); db.Expenses.Add(new Expense { ExpenseId = 1, DocNo = "EXP-1", BranchId = Branch1, ExpenseDate = day, Amount = 100, Status = ExpenseStatus.Posted }); db.ExpensePayments.AddRange( new ExpensePayment { ExpensePaymentId = 1, ExpenseId = 1, PaymentDate = At(day), PaymentMethod = ExpensePaymentMethod.Cash, Amount = 40 }, new ExpensePayment { ExpensePaymentId = 2, ExpenseId = 1, PaymentDate = At(day), PaymentMethod = ExpensePaymentMethod.BankTransfer, Amount = 60 }); await db.SaveChangesAsync(); var p = (await CashService(db, Start).GetPositionAsync(day, Branch1)).Position; Assert.Equal(450m, p.CashIn); Assert.Equal(40m, p.CashOut); Assert.Equal(410m, p.NetCash); Assert.Equal(410m, p.Closing); } [Fact] public async Task UndepositedCash_CarriesToTheNextDay_AndDepositsReduceIt() { await using var db = CreateContext(); var d1 = Today.AddDays(-3); var d2 = Today.AddDays(-2); db.SalesSlips.AddRange(CashSlip(d1, 1000), CashSlip(d2, 500)); db.BankDeposits.AddRange( Deposit(d1, 600), Deposit(d1, 999, status: BankDepositStatus.Cancelled)); // ignored await db.SaveChangesAsync(); var days = (await CashService(db, Start).GetDailyAsync(d1, d2, Branch1)).Days; Assert.Equal((0m, 1000m, 600m, 400m), (days[0].Opening, days[0].NetCash, days[0].Deposited, days[0].Closing)); Assert.Equal((400m, 500m, 0m, 900m), (days[1].Opening, days[1].NetCash, days[1].Deposited, days[1].Closing)); } [Fact] public async Task Opening_IsCarriedInFromBeforeTheWindow() { await using var db = CreateContext(); db.SalesSlips.AddRange(CashSlip(Today.AddDays(-6), 700), CashSlip(Today.AddDays(-5), 300)); db.BankDeposits.Add(Deposit(Today.AddDays(-5), 200)); await db.SaveChangesAsync(); var day = (await CashService(db, Start).GetPositionAsync(Today.AddDays(-1), Branch1)).Position; Assert.Equal(800m, day.Opening); Assert.Equal(800m, day.Closing); } [Fact] public async Task CashBeforeTheTrackingStart_IsNotCarried() { await using var db = CreateContext(); db.SalesSlips.AddRange(CashSlip(Start.AddDays(-1), 5000), CashSlip(Start, 100)); await db.SaveChangesAsync(); var days = (await CashService(db, Start).GetDailyAsync(Start.AddDays(-1), Start, Branch1)).Days; Assert.Equal(5000m, days[0].NetCash); // still reported for the day itself Assert.Equal(0m, days[1].Opening); // but not carried into tracking Assert.Equal(100m, days[1].Closing); } [Fact] public async Task Available_IsTheLowestClosingFromThatDayToToday() { await using var db = CreateContext(); var d1 = Today.AddDays(-2); db.SalesSlips.Add(CashSlip(d1, 1000)); db.BankDeposits.Add(Deposit(Today.AddDays(-1), 800)); // already took most of it the next day await db.SaveChangesAsync(); var position = await CashService(db, Start).GetPositionAsync(d1, Branch1); Assert.Equal(1000m, position.Position.Closing); Assert.Equal(200m, position.AvailableToDeposit); } [Fact] public async Task Branches_AreKeptApart() { await using var db = CreateContext(); var day = Today.AddDays(-1); db.SalesSlips.AddRange(CashSlip(day, 100, warehouseId: Warehouse1), CashSlip(day, 900, warehouseId: Warehouse2)); await db.SaveChangesAsync(); var service = CashService(db, Start); Assert.Equal(100m, (await service.GetPositionAsync(day, Branch1)).Position.Closing); Assert.Equal(900m, (await service.GetPositionAsync(day, Branch2)).Position.Closing); Assert.Equal(1000m, (await service.GetPositionAsync(day, null)).Position.Closing); } [Fact] public async Task LateEveningSale_CountsOnTheLocalDay() { await using var db = CreateContext(); var day = Today.AddDays(-1); var slip = CashSlip(day, 250); slip.SlipDate = Clock.ToUtc(day.AddDays(1)).AddMinutes(-30); // 23:30 local db.SalesSlips.Add(slip); await db.SaveChangesAsync(); var days = (await CashService(db, Start).GetDailyAsync(day, day.AddDays(1), Branch1)).Days; Assert.Equal(250m, days[0].CashIn); Assert.Equal(0m, days[1].CashIn); } }