diff --git a/.claude/worktrees/agent-a98805dc618e0f67c b/.claude/worktrees/agent-a98805dc618e0f67c deleted file mode 160000 index b4993b2..0000000 --- a/.claude/worktrees/agent-a98805dc618e0f67c +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b4993b202deed0c14f4739b3ce363e8c63bb91b4 diff --git a/Backend/ERPCore.Tests/BankDepositServiceTests.cs b/Backend/ERPCore.Tests/BankDepositServiceTests.cs new file mode 100644 index 0000000..837aaf7 --- /dev/null +++ b/Backend/ERPCore.Tests/BankDepositServiceTests.cs @@ -0,0 +1,211 @@ +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Accounts; +using ERPCore.System.Errors; +using ERPCore.Tests.TestSupport; +using Microsoft.EntityFrameworkCore; +using static ERPCore.Tests.TestSupport.BankDepositTestSupport; + +namespace ERPCore.Tests; + +/// Recording and cancelling bank deposits (docs/BANK_DEPOSIT_MODULE_PLAN.md §7.2, §11). +public class BankDepositServiceTests +{ + private static readonly DateOnly Today = Clock.Today; + private static readonly DateOnly Start = Today.AddDays(-10); + private static readonly DateOnly Day = Today.AddDays(-2); + + [Fact] + public async Task Create_PostsBankDebitCashCredit_AndLowersCashInHand() + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000)); + await db.SaveChangesAsync(); + var gl = new FakeGeneralLedger(); + + var dto = await DepositService(db, Start, gl).CreateAsync(Request(600, depositDate: Day.AddDays(1))); + + Assert.Equal(BankDepositStatus.Posted, dto.Status); + Assert.Equal(("BOC Current", "Bank of Ceylon", "0001234567"), (dto.ToBankAccountName, dto.ToBankName, dto.ToAccountNumber)); + Assert.Equal("JE-1", dto.GlJournalNo); + + var entry = Assert.Single(gl.Posted); + Assert.Equal(Day.AddDays(1), entry.EntryDate); + Assert.Equal("BANK_DEPOSIT", entry.SourceModule); + Assert.Collection(entry.Lines, + l => Assert.Equal(("1010-01", 600m, 0m), (l.AccountCode, l.DebitAmount, l.CreditAmount)), + l => Assert.Equal(("1000-01", 0m, 600m), (l.AccountCode, l.DebitAmount, l.CreditAmount))); + + var position = await CashService(db, Start).GetPositionAsync(Day, Branch1); + Assert.Equal((600m, 400m), (position.Position.Deposited, position.Position.Closing)); + } + + [Fact] + public async Task Create_MoreThanCashInHand_IsRejected() + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000, card: 5000)); + await db.SaveChangesAsync(); + var gl = new FakeGeneralLedger(); + + var ex = await Assert.ThrowsAsync(() => DepositService(db, Start, gl).CreateAsync(Request(1000.01m))); + + Assert.Equal(ErrorCodes.BankDepositExceedsCashInHand, ex.Code); + Assert.Empty(gl.Posted); + } + + [Fact] + public async Task Create_BackDatedDepositThatOverdrawsALaterDay_IsRejected() + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000)); + db.BankDeposits.Add(Deposit(Day.AddDays(1), 900)); // the next day already banked most of it + await db.SaveChangesAsync(); + + var ex = await Assert.ThrowsAsync(() => + DepositService(db, Start, new FakeGeneralLedger()).CreateAsync(Request(500))); + + Assert.Equal(ErrorCodes.BankDepositExceedsCashInHand, ex.Code); + Assert.Contains("100.00", ex.Message); + } + + [Fact] + public async Task Cancel_ReversesGl_AndPutsTheCashBack() + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000)); + await db.SaveChangesAsync(); + var gl = new FakeGeneralLedger(); + var service = DepositService(db, Start, gl); + var created = await service.CreateAsync(Request(1000)); + + var cancelled = await service.CancelAsync(created.BankDepositId, new CancelBankDepositRequest { Reason = " wrong bank " }); + + Assert.Equal(BankDepositStatus.Cancelled, cancelled.Status); + Assert.Equal("wrong bank", cancelled.CancelReason); + Assert.Equal("JE-2", cancelled.GlReversalJournalNo); + Assert.Collection(gl.Posted[1].Lines, + l => Assert.Equal(("1000-01", 1000m, 0m), (l.AccountCode, l.DebitAmount, l.CreditAmount)), + l => Assert.Equal(("1010-01", 0m, 1000m), (l.AccountCode, l.DebitAmount, l.CreditAmount))); + Assert.Equal(1000m, (await CashService(db, Start).GetPositionAsync(Day, Branch1)).Position.Closing); + + var again = await Assert.ThrowsAsync(() => + service.CancelAsync(created.BankDepositId, new CancelBankDepositRequest { Reason = "again" })); + Assert.Equal(ErrorCodes.BankDepositNotCancellable, again.Code); + } + + [Theory] + [InlineData(FakeGeneralLedger.BankAccountId, FakeGeneralLedger.BankAccountId, ErrorCodes.BankDepositCashAccountInvalid)] + [InlineData(FakeGeneralLedger.CashAccountId, FakeGeneralLedger.CashAccountId, ErrorCodes.BankDepositBankAccountInvalid)] + [InlineData(99L, FakeGeneralLedger.BankAccountId, ErrorCodes.BankDepositCashAccountInvalid)] + public async Task Create_WrongAccountTypes_AreRejected(long from, long to, string code) + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000)); + await db.SaveChangesAsync(); + + var request = Request(100); + request.FromGlCashAccountId = from; + request.ToGlBankAccountId = to; + var ex = await Assert.ThrowsAsync(() => DepositService(db, Start, new FakeGeneralLedger()).CreateAsync(request)); + + Assert.Equal(code, ex.Code); + } + + [Fact] + public async Task Create_CashAndBankAccountsSharingAnId_AreToldApartByType() + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000)); + await db.SaveChangesAsync(); + // GL numbers cash and bank accounts in separate tables, so both can be account 1. + var gl = new FakeGeneralLedger + { + Accounts = + [ + new("Cash", 1, "main", null, "Cash in Transit", "CASH-000001", 22, "1400-01-01", "LKR"), + new("Bank", 1, "Main - 01", "Commercial Bank", null, "2032434543", 2, "1000-01", "LKR") + ] + }; + var request = Request(400); + request.FromGlCashAccountId = 1; + request.ToGlBankAccountId = 1; + + var dto = await DepositService(db, Start, gl).CreateAsync(request); + + Assert.Equal(("main", "Main - 01"), (dto.FromCashAccountName, dto.ToBankAccountName)); + Assert.Collection(Assert.Single(gl.Posted).Lines, + l => Assert.Equal(("1000-01", 400m), (l.AccountCode, l.DebitAmount)), + l => Assert.Equal(("1400-01-01", 400m), (l.AccountCode, l.CreditAmount))); + } + + [Fact] + public async Task Create_CannotUseAnotherBranchsCash() + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000, warehouseId: Warehouse2)); + await db.SaveChangesAsync(); + + var ex = await Assert.ThrowsAsync(() => + DepositService(db, Start, new FakeGeneralLedger()).CreateAsync(Request(100, branchId: Branch1))); + + Assert.Equal(ErrorCodes.BankDepositExceedsCashInHand, ex.Code); + } + + [Theory] + [InlineData(-11, null)] // before tracking starts + [InlineData(1, null)] // business date in the future + [InlineData(-2, -3)] // deposited before the business day + [InlineData(-2, 2)] // deposit date beyond tomorrow + public async Task Create_InvalidDates_AreRejected(int businessOffset, int? depositOffset) + { + await using var db = CreateContext(); + var request = Request(100); + request.BusinessDate = Today.AddDays(businessOffset); + request.DepositDate = depositOffset is { } d ? Today.AddDays(d) : null; + + var ex = await Assert.ThrowsAsync(() => DepositService(db, Start, new FakeGeneralLedger()).CreateAsync(request)); + + Assert.Equal(ErrorCodes.BankDepositDateInvalid, ex.Code); + } + + [Fact] + public async Task Create_GlRejection_LeavesNoDeposit() + { + await using var db = CreateContext(); + db.SalesSlips.Add(CashSlip(Day, 1000)); + await db.SaveChangesAsync(); + + await Assert.ThrowsAsync(() => + DepositService(db, Start, new FakeGeneralLedger { Reject = true }).CreateAsync(Request(500))); + + Assert.Equal(0, await db.BankDeposits.CountAsync()); + } + + [Fact] + public async Task List_FiltersAndTotalsEveryMatchingRow() + { + await using var db = CreateContext(); + db.BankDeposits.AddRange( + Deposit(Day, 100), Deposit(Day, 200), Deposit(Day.AddDays(-1), 400), + Deposit(Day, 800, branchId: Branch2), Deposit(Day, 1600, status: BankDepositStatus.Cancelled)); + await db.SaveChangesAsync(); + + var result = await DepositService(db, Start, new FakeGeneralLedger()).ListAsync( + new() { PageSize = 1 }, new BankDepositListFilter(Day, Day, Branch1, null, BankDepositStatus.Posted)); + + Assert.Single(result.Items); + Assert.Equal(2, result.Pagination.TotalItems); + Assert.Equal(300m, result.TotalAmount); + } + + private static CreateBankDepositRequest Request(decimal amount, int branchId = Branch1, DateOnly? depositDate = null) => new() + { + BranchId = branchId, + BusinessDate = Day, + DepositDate = depositDate, + Amount = amount, + FromGlCashAccountId = FakeGeneralLedger.CashAccountId, + ToGlBankAccountId = FakeGeneralLedger.BankAccountId, + DepositSlipNo = "SLIP-1" + }; +} diff --git a/Backend/ERPCore.Tests/CashInHandServiceTests.cs b/Backend/ERPCore.Tests/CashInHandServiceTests.cs new file mode 100644 index 0000000..0241c10 --- /dev/null +++ b/Backend/ERPCore.Tests/CashInHandServiceTests.cs @@ -0,0 +1,134 @@ +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); + } +} diff --git a/Backend/ERPCore.Tests/DayEndReportServiceTests.cs b/Backend/ERPCore.Tests/DayEndReportServiceTests.cs new file mode 100644 index 0000000..bf9613c --- /dev/null +++ b/Backend/ERPCore.Tests/DayEndReportServiceTests.cs @@ -0,0 +1,257 @@ +using ERPCore.Domain; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Infra.Auth; +using ERPCore.Infra.Persistence; +using ERPCore.Repositories; +using ERPCore.Services; +using ERPCore.System.Errors; +using ERPCore.Tests.TestSupport; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.Configuration; + +namespace ERPCore.Tests; + +/// Aggregates of the Day-End Sales Report (docs/DAY_END_SALES_REPORT_PLAN.md §7). +public class DayEndReportServiceTests +{ + private static readonly InMemoryDatabaseRoot Root = new(); + + // Asia/Colombo is UTC+05:30 all year (no DST), so local midnight is 18:30 UTC the day before. + private static readonly DateOnly Day = new(2026, 10, 8); + private static DateTime UtcAt(int hour, int minute = 0) => new(2026, 10, 8, hour, minute, 0, DateTimeKind.Utc); + + [Fact] + public async Task Day_MixOfSlipsBundlesAndInvoices_TotalsAndSplitsByMethod() + { + await using var db = CreateContext(); + db.SalesSlips.AddRange( + Slip(1, UtcAt(5), total: 1000, paid: 600, cash: 400, card: 200), // partial, mixed + Slip(2, UtcAt(6), total: 200, paid: 200, cash: 200, card: 0), // all cash + Slip(3, UtcAt(7), total: 999, paid: 999, cash: 999, card: 0, status: SalesSlipStatus.Draft)); + db.BundleSales.Add(new BundleSale { BundleSaleId = 1, BundleDate = UtcAt(8), Status = BundleSaleStatus.Posted, GrandTotal = 500, CashAmount = 0, CardAmount = 500 }); + db.SalesInvoices.Add(Invoice(1, UtcAt(9), netPayable: 2000, paid: 1500)); + db.SalesInvoicePayments.AddRange( + Payment(1, invoiceId: 1, UtcAt(9), SalesPaymentMethod.Cash, 500), + Payment(2, invoiceId: 1, UtcAt(10), SalesPaymentMethod.Card, 1000)); + SeedExpenseSetup(db); + db.Expenses.AddRange( + Expense(1, Day, 300, ExpenseStatus.Posted), + Expense(2, Day, 700, ExpenseStatus.Cancelled), + Expense(3, Day.AddDays(1), 50, ExpenseStatus.Posted)); + await db.SaveChangesAsync(); + + var report = await CreateService(db).GetDayAsync(Day, null); + var s = report.Summary; + + Assert.Equal(3700m, s.TotalSales); + Assert.Equal(2800m, s.PaidAmount); + Assert.Equal(900m, s.DueAmount); + Assert.Equal(1100m, s.PaidByCash); + Assert.Equal(1700m, s.PaidByCard); + Assert.Equal(0m, s.PaidByOther); + Assert.Equal(4, s.SalesCount); + Assert.Equal(1, s.CashSalesCount); + Assert.Equal(1, s.CardSalesCount); + Assert.Equal(2, s.MixedSalesCount); + Assert.Equal(0, s.UnpaidSalesCount); + Assert.Equal(300m, s.ExpenseTotal); + Assert.Equal(3400m, s.Income); + Assert.Single(report.Expenses); + Assert.Equal("Electricity", report.Expenses[0].TitleName); + } + + [Fact] + public async Task Day_SaleLateInTheLocalEvening_CountsOnTheLocalDay() + { + await using var db = CreateContext(); + // 19:00 UTC on Oct 7 is 00:30 on Oct 8 in Colombo; 18:00 UTC on Oct 8 is 23:30 on Oct 8. + db.SalesSlips.AddRange( + Slip(1, new DateTime(2026, 10, 7, 19, 0, 0, DateTimeKind.Utc), total: 100, paid: 100, cash: 100, card: 0), + Slip(2, new DateTime(2026, 10, 8, 18, 0, 0, DateTimeKind.Utc), total: 40, paid: 40, cash: 40, card: 0), + Slip(3, new DateTime(2026, 10, 8, 19, 0, 0, DateTimeKind.Utc), total: 7, paid: 7, cash: 7, card: 0)); + await db.SaveChangesAsync(); + + var report = await CreateService(db).GetDailyAsync(Day.AddDays(-1), Day.AddDays(1), null); + + Assert.Equal([0m, 140m, 7m], report.Days.Select(d => d.TotalSales)); + Assert.Equal(147m, report.Totals.TotalSales); + } + + [Fact] + public async Task Day_PaymentOnAnEarlierInvoice_IsACollectionAndCountsInNetCash() + { + await using var db = CreateContext(); + db.SalesInvoices.Add(Invoice(1, UtcAt(5).AddDays(-3), netPayable: 800, paid: 250)); + db.SalesInvoicePayments.Add(Payment(1, invoiceId: 1, UtcAt(6), SalesPaymentMethod.Cash, 250)); + db.SalesSlips.Add(Slip(1, UtcAt(7), total: 60, paid: 60, cash: 60, card: 0)); + SeedExpenseSetup(db); + db.Expenses.Add(Expense(1, Day, 30, ExpenseStatus.Posted)); + db.ExpensePayments.Add(new ExpensePayment { ExpensePaymentId = 1, ExpenseId = 1, Amount = 30, PaymentDate = UtcAt(8), PaymentMethod = ExpensePaymentMethod.Cash }); + await db.SaveChangesAsync(); + + var s = (await CreateService(db).GetDayAsync(Day, null)).Summary; + + Assert.Equal(60m, s.TotalSales); + Assert.Equal(60m, s.PaidAmount); + Assert.Equal(250m, s.EarlierInvoiceCollections); + Assert.Equal(60m + 250m - 30m, s.NetCash); + } + + [Fact] + public async Task Day_ShowsBankDepositsAndTheCashLeftInHand() + { + await using var db = CreateContext(); + db.SalesSlips.AddRange( + Slip(1, UtcAt(5).AddDays(-1), total: 300, paid: 300, cash: 300, card: 0), // yesterday, never banked + Slip(2, UtcAt(6), total: 1000, paid: 1000, cash: 1000, card: 0)); + db.BankDeposits.AddRange( + BankDeposit(1, Day, 800, BankDepositStatus.Posted), + BankDeposit(2, Day, 5000, BankDepositStatus.Cancelled)); + await db.SaveChangesAsync(); + + var report = await CreateService(db).GetDayAsync(Day, null); + var s = report.Summary; + + Assert.Equal((300m, 1000m, 800m, 500m), (s.OpeningCashInHand, s.NetCash, s.CashDeposited, s.ClosingCashInHand)); + var deposit = Assert.Single(report.Deposits); + Assert.Equal(("BD-1", "BOC Current", 800m), (deposit.DocNo, deposit.BankAccountName, deposit.Amount)); + + var range = await CreateService(db).GetDailyAsync(Day.AddDays(-1), Day, null); + Assert.Equal((0m, 800m, 500m), (range.Totals.OpeningCashInHand, range.Totals.CashDeposited, range.Totals.ClosingCashInHand)); + } + + [Fact] + public async Task Day_Profit_IsSalesLessStockCostLessExpenses_AndCanBeALoss() + { + await using var db = CreateContext(); + db.SalesInvoices.Add(Invoice(1, UtcAt(5), netPayable: 1000, paid: 1000)); + db.SalesInvoicePayments.Add(Payment(1, invoiceId: 1, UtcAt(5), SalesPaymentMethod.Card, 1000)); + db.SalesSlips.Add(Slip(1, UtcAt(6), total: 200, paid: 0, cash: 0, card: 0)); + db.StockLedger.AddRange( + Ledger(1, DocumentTypes.SalesInvoice, 1, Direction.Out, 600), + Ledger(2, DocumentTypes.SalesInvoice, 1, Direction.Out, 100), + Ledger(3, DocumentTypes.SalesSlip, 1, Direction.Out, 150), + Ledger(4, DocumentTypes.SalesInvoice, 1, Direction.In, 999)); // not a sale's cost + SeedExpenseSetup(db); + db.Expenses.Add(Expense(1, Day, 900, ExpenseStatus.Posted)); + await db.SaveChangesAsync(); + + var report = await CreateService(db).GetDayAsync(Day, null); + var s = report.Summary; + + Assert.Equal(850m, s.CostOfGoodsSold); + Assert.Equal(350m, s.GrossProfit); + Assert.Equal(-550m, s.NetProfit); + Assert.Equal(2, report.Sales.Count); + var invoice = report.Sales.Single(x => x.DocType == "Invoice"); + Assert.Equal(("Card", 700m, 300m), (invoice.PaidBy, invoice.Cost, invoice.GrossProfit)); + Assert.Equal("Unpaid", report.Sales.Single(x => x.DocType == "Slip").PaidBy); + } + + [Fact] + public async Task Day_UnpaidInvoice_CountsAsUnpaidAndDue() + { + await using var db = CreateContext(); + db.SalesInvoices.Add(Invoice(1, UtcAt(5), netPayable: 450, paid: 0)); + await db.SaveChangesAsync(); + + var s = (await CreateService(db).GetDayAsync(Day, null)).Summary; + + Assert.Equal(1, s.UnpaidSalesCount); + Assert.Equal(450m, s.DueAmount); + } + + [Fact] + public async Task Daily_FillsQuietDaysWithZeros() + { + await using var db = CreateContext(); + var report = await CreateService(db).GetDailyAsync(Day, Day.AddDays(4), null); + + Assert.Equal(5, report.Days.Count); + Assert.All(report.Days, d => Assert.Equal(0m, d.Income)); + Assert.Equal(Day.AddDays(4), report.Days[^1].Date); + } + + [Theory] + [InlineData(1, 0)] // to before from + [InlineData(0, 92)] // 93 days + public async Task Daily_InvalidRange_IsRejected(int fromOffset, int toOffset) + { + await using var db = CreateContext(); + var ex = await Assert.ThrowsAsync(() => + CreateService(db).GetDailyAsync(Day.AddDays(fromOffset), Day.AddDays(toOffset), null)); + + Assert.Equal(ErrorCodes.DayEndReportRangeInvalid, ex.Code); + } + + // --- helpers ------------------------------------------------------------------------------ + + private static SalesSlip Slip(int id, DateTime at, decimal total, decimal paid, decimal cash, decimal card, + SalesSlipStatus status = SalesSlipStatus.Posted) => new() + { + SalesSlipId = id, SlipNo = $"SL-{id}", SlipDate = at, Status = status, + GrandTotal = total, PaidAmount = paid, BalanceAmount = total - paid, CashAmount = cash, CardAmount = card + }; + + private static SalesInvoice Invoice(int id, DateTime at, decimal netPayable, decimal paid) => new() + { + SalesInvoiceId = id, InvoiceNo = $"INV-{id}", InvoiceDate = at, Status = SalesInvoiceStatus.Posted, + GrandTotal = netPayable, NetPayable = netPayable, PaidAmount = paid, BalanceAmount = netPayable - paid + }; + + private static SalesInvoicePayment Payment(int id, int invoiceId, DateTime at, SalesPaymentMethod method, decimal amount) => new() + { + SalesInvoicePaymentId = id, SalesInvoiceId = invoiceId, PaymentDate = at, PaymentMethod = method, Amount = amount + }; + + private static StockLedger Ledger(int id, string docType, int docId, Direction direction, decimal value) => new() + { + LedgerId = id, SourceDocType = docType, SourceDocId = docId, Direction = direction, Value = value, QtyBase = 1, ItemId = 1, WarehouseId = 1 + }; + + private static Expense Expense(int id, DateOnly date, decimal amount, ExpenseStatus status) => new() + { + ExpenseId = id, DocNo = $"EXP-{id}", ExpenseDate = date, Amount = amount, Status = status, + ExpenseCategoryId = 1, ExpenseTitleId = 1 + }; + + private static BankDeposit BankDeposit(int id, DateOnly businessDate, decimal amount, BankDepositStatus status) => new() + { + BankDepositId = id, DocNo = $"BD-{id}", BranchId = 1, BusinessDate = businessDate, DepositDate = businessDate, Amount = amount, + Status = status, FromCashAccountCode = "1000-01", FromCashAccountName = "Main Till", + ToBankAccountCode = "1010-01", ToBankAccountName = "BOC Current" + }; + + private static void SeedExpenseSetup(ErpDbContext db) + { + db.ExpenseCategories.Add(new ExpenseCategory { ExpenseCategoryId = 1, Code = "UTILITY", Name = "Utility" }); + db.ExpenseTitles.Add(new ExpenseTitle { ExpenseTitleId = 1, ExpenseCategoryId = 1, Name = "Electricity", GlExpenseAccountCode = "5200-01" }); + } + + private static DayEndReportService CreateService(ErpDbContext db) + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { ["DayEndReport:TimeZone"] = "Asia/Colombo" }) + .Build(); + var clock = new BusinessDayClock(config); + var branch = new CurrentBranchContext(new TestCurrentUser()); + var cash = new CashInHandService( + new Repository(db), new Repository(db), new Repository(db), + new Repository(db), new Repository(db), branch, clock, config); + return new DayEndReportService( + new Repository(db), new Repository(db), new Repository(db), + new Repository(db), new Repository(db), + new Repository(db), new Repository(db), cash, branch, clock); + } + + private static ErpDbContext CreateContext() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(Guid.NewGuid().ToString("N"), Root) + .Options; + var currentUser = new TestCurrentUser(); + return new ErpDbContext(options, currentUser, new CurrentBranchContext(currentUser)); + } +} diff --git a/Backend/ERPCore.Tests/DualApprovalTests.cs b/Backend/ERPCore.Tests/DualApprovalTests.cs new file mode 100644 index 0000000..222b69b --- /dev/null +++ b/Backend/ERPCore.Tests/DualApprovalTests.cs @@ -0,0 +1,388 @@ +using ERPCore.Domain; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Approvals; +using ERPCore.Dtos.Notifications; +using ERPCore.Dtos.Products; +using ERPCore.Dtos.Stock; +using ERPCore.Infra.Auth; +using ERPCore.Infra.Persistence; +using ERPCore.Repositories; +using ERPCore.Services; +using ERPCore.Services.Interfaces; +using ERPCore.Services.Stock; +using ERPCore.System.Errors; +using ERPCore.Tests.TestSupport; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.Configuration; + +namespace ERPCore.Tests; + +/// Dual approval of stock and price adjustments (docs/DUAL_APPROVAL_PLAN.md §13). +public class DualApprovalTests +{ + private static readonly InMemoryDatabaseRoot Root = new(); + + // Requester, two approvers, and a clerk who can approve but isn't a manager. + private const int Requester = 10, ApproverA = 11, ApproverB = 12, Clerk = 13; + private const int ApproverRoleId = 2, ClerkRoleId = 3; + private const int ItemId = 1, WarehouseId = 1, ReasonId = 1; + + // --- Stock adjustments ------------------------------------------------------------------ + + [Fact] + public async Task StockAdjustment_IsARequest_NothingMovesUntilApproved() + { + var h = await Harness.CreateAsync(); + + var dto = await h.Stock.CreateAsync(Request(-3)); + + Assert.Equal(AdjustmentStatus.PendingApproval, dto.Status); + Assert.Equal(2, dto.RequiredApprovals); + Assert.Equal(1, dto.PendingLevel); + Assert.Empty(h.Mutator.Calls); + Assert.Empty(h.Gl.Posted); + var pending = Assert.Single(h.Notifications.Targeted, n => n.ModuleKey == "approval.pending"); + Assert.Equal([ApproverA, ApproverB, Clerk], pending.UserIds.Order()); // never the requester + } + + [Fact] + public async Task StockAdjustment_TwoDifferentApprovers_ThenItPosts() + { + var h = await Harness.CreateAsync(); + var id = (await h.Stock.CreateAsync(Request(-3))).AdjustmentId; + + h.User.Be(Requester); + var own = await Assert.ThrowsAsync(() => h.Stock.ApproveAsync(id, null)); + Assert.Equal(ErrorCodes.ApprovalForbidden, own.Code); + + h.User.Be(ApproverA); + var first = await h.Stock.ApproveAsync(id, "counted twice"); + Assert.Equal(AdjustmentStatus.PendingSecondApproval, first.Status); + Assert.Empty(h.Mutator.Calls); + + var twice = await Assert.ThrowsAsync(() => h.Stock.ApproveAsync(id, null)); + Assert.Equal(ErrorCodes.ApprovalSamePerson, twice.Code); + + h.User.Be(ApproverB); + var posted = await h.Stock.ApproveAsync(id, null); + + Assert.Equal(AdjustmentStatus.Posted, posted.Status); + Assert.Equal((ApproverA, ApproverB), (posted.ApprovedLevel1By, posted.ApprovedLevel2By)); + var call = Assert.Single(h.Mutator.Calls); + Assert.Equal(-3m, Assert.Single(call).QtyDelta); + Assert.Single(h.Gl.Posted); + Assert.NotNull(posted.PostedAt); + Assert.Equal([1, 2], posted.ApprovalHistory!.Select(s => s.Level)); + Assert.Contains(h.Notifications.Targeted, n => n.ModuleKey == "approval.approved" && n.UserIds.SequenceEqual([Requester])); + } + + [Fact] + public async Task StockAdjustment_Rejected_NeedsAReason_AndNeverPosts() + { + var h = await Harness.CreateAsync(); + var id = (await h.Stock.CreateAsync(Request(5))).AdjustmentId; + h.User.Be(ApproverA); + + var noReason = await Assert.ThrowsAsync(() => h.Stock.RejectAsync(id, " ")); + Assert.Equal(ErrorCodes.ApprovalReasonRequired, noReason.Code); + + var rejected = await h.Stock.RejectAsync(id, "wrong warehouse"); + Assert.Equal((AdjustmentStatus.Rejected, "wrong warehouse"), (rejected.Status, rejected.RejectReason)); + + h.User.Be(ApproverB); + var late = await Assert.ThrowsAsync(() => h.Stock.ApproveAsync(id, null)); + Assert.Equal(ErrorCodes.ApprovalNotPending, late.Code); + Assert.Empty(h.Mutator.Calls); + Assert.Contains(h.Notifications.Targeted, n => n.ModuleKey == "approval.rejected"); + } + + [Fact] + public async Task StockAdjustment_ApprovalsOff_PostsOnCreate() + { + var h = await Harness.CreateAsync(stockLevels: 0); + + var dto = await h.Stock.CreateAsync(Request(2)); + + Assert.Equal((AdjustmentStatus.Posted, 0), (dto.Status, dto.RequiredApprovals)); + Assert.Single(h.Mutator.Calls); + } + + [Fact] + public async Task StockAdjustment_BelowTheValueThreshold_NeedsOneApproval() + { + // 3 units at 10.00 each = 30.00, below the 1,000 threshold. + var h = await Harness.CreateAsync(stockThreshold: 1000); + var id = (await h.Stock.CreateAsync(Request(-3))).AdjustmentId; + + h.User.Be(ApproverA); + var posted = await h.Stock.ApproveAsync(id, null); + + Assert.Equal((AdjustmentStatus.Posted, 1), (posted.Status, posted.RequiredApprovals)); + Assert.Single(h.Mutator.Calls); + } + + [Fact] + public async Task StockAdjustment_OnlyTheRequesterCanWithdraw() + { + var h = await Harness.CreateAsync(); + var id = (await h.Stock.CreateAsync(Request(-1))).AdjustmentId; + + h.User.Be(ApproverA); + var other = await Assert.ThrowsAsync(() => h.Stock.CancelAsync(id)); + Assert.Equal(ErrorCodes.ApprovalForbidden, other.Code); + + h.User.Be(Requester); + Assert.Equal(AdjustmentStatus.Cancelled, (await h.Stock.CancelAsync(id)).Status); + } + + [Fact] + public async Task SecondApproval_CanBeLimitedToRoles() + { + var h = await Harness.CreateAsync(level2Roles: "Approver"); + var id = (await h.Stock.CreateAsync(Request(-1))).AdjustmentId; + h.User.Be(ApproverA); + await h.Stock.ApproveAsync(id, null); + + h.User.Be(Clerk); + var clerk = await Assert.ThrowsAsync(() => h.Stock.ApproveAsync(id, null)); + Assert.Equal(ErrorCodes.ApprovalRoleRequired, clerk.Code); + + h.User.Be(ApproverB); + Assert.Equal(AdjustmentStatus.Posted, (await h.Stock.ApproveAsync(id, null)).Status); + } + + // --- Price adjustments ------------------------------------------------------------------ + + [Fact] + public async Task PriceAdjustment_AppliesOnlyAfterTheSecondApproval_AndKeepsHistory() + { + var h = await Harness.CreateAsync(); + var dto = await h.Prices.CreateAsync(PriceRequest(flat: 120m)); + Assert.Equal(PriceAdjustmentStatus.PendingApproval, dto.Status); + Assert.Equal(100m, (await h.Db.Items.AsNoTracking().FirstAsync()).SellingFlatPrice); + + h.User.Be(ApproverA); + await h.Prices.ApproveAsync(dto.PriceAdjustmentId, null); + Assert.Equal(100m, (await h.Db.Items.AsNoTracking().FirstAsync()).SellingFlatPrice); + + h.User.Be(ApproverB); + var applied = await h.Prices.ApproveAsync(dto.PriceAdjustmentId, null); + + Assert.Equal(PriceAdjustmentStatus.Approved, applied.Status); + Assert.Equal(120m, (await h.Db.Items.AsNoTracking().FirstAsync()).SellingFlatPrice); + var history = Assert.Single(await h.Prices.ItemHistoryAsync(ItemId)); + Assert.Equal((100m, 120m, ApproverA, ApproverB, dto.DocNo), (history.OldFlatPrice, history.NewFlatPrice, history.ApprovedLevel1By, history.ApprovedLevel2By, history.DocNo)); + } + + [Fact] + public async Task PriceAdjustment_ItemRepricedSinceTheRequest_IsRefusedAsStale() + { + var h = await Harness.CreateAsync(); + var id = (await h.Prices.CreateAsync(PriceRequest(flat: 120m))).PriceAdjustmentId; + h.User.Be(ApproverA); + await h.Prices.ApproveAsync(id, null); + + var item = await h.Db.Items.FirstAsync(); + item.SellingFlatPrice = 105m; // someone changed it in between + await h.Db.SaveChangesAsync(); + + h.User.Be(ApproverB); + var stale = await Assert.ThrowsAsync(() => h.Prices.ApproveAsync(id, null)); + Assert.Equal(ErrorCodes.PriceAdjustmentStale, stale.Code); + Assert.Empty(await h.Prices.ItemHistoryAsync(ItemId)); + } + + [Fact] + public async Task PriceAdjustment_OnePendingRequestPerItem() + { + var h = await Harness.CreateAsync(); + await h.Prices.CreateAsync(PriceRequest(flat: 120m)); + + var second = await Assert.ThrowsAsync(() => h.Prices.CreateAsync(PriceRequest(flat: 130m))); + Assert.Equal(ErrorCodes.PriceAdjustmentItemPending, second.Code); + } + + [Theory] + [InlineData(100, 120, 20)] + [InlineData(100, 80, 20)] + [InlineData(-1, 50, 100)] // setting a price where there was none (-1 = no price) + [InlineData(50, -1, 100)] // clearing one + [InlineData(40, 40, 0)] + public void ChangePercent_MeasuresTheSizeOfAPriceChange(int oldPrice, int newPrice, int expected) + { + decimal? Price(int v) => v < 0 ? null : v; + Assert.Equal(expected, PriceAdjustmentService.ChangePercent(Price(oldPrice), Price(newPrice))); + } + + [Theory] + [InlineData(2, 0, 5000, 2)] // no threshold: always two + [InlineData(2, 1000, 50, 1)] // below threshold: one + [InlineData(2, 1000, 1000, 2)] // at threshold: two + [InlineData(1, 1000, 5000, 1)] + [InlineData(0, 0, 5000, 0)] + public void RequiredLevels_FollowTheSettings(int configured, double threshold, double measure, int expected) + => Assert.Equal(expected, ApprovalService.Levels(configured, (decimal)threshold, (decimal)measure)); + + // --- helpers ------------------------------------------------------------------------------ + + private static CreateAdjustmentRequest Request(decimal qty) => new() + { + WarehouseId = WarehouseId, BranchId = 1, ReasonCodeId = ReasonId, + Lines = [new CreateAdjustmentLineInput { ItemId = ItemId, QtyDelta = qty }] + }; + + private static CreatePriceAdjustmentRequest PriceRequest(decimal flat) => new() + { + Reason = "Supplier price rise", + Lines = [new PriceAdjustmentLineInput { ItemId = ItemId, NewMinPrice = 90m, NewMaxPrice = 150m, NewFlatPrice = flat }] + }; + + private sealed class Harness + { + public required ErpDbContext Db { get; init; } + public required SwitchableUser User { get; init; } + public required RecordingMutator Mutator { get; init; } + public required FakeGeneralLedger Gl { get; init; } + public required RecordingNotifications Notifications { get; init; } + public required AdjustmentService Stock { get; init; } + public required PriceAdjustmentService Prices { get; init; } + + public static async Task CreateAsync(int stockLevels = 2, decimal stockThreshold = 0, string? level2Roles = null) + { + var db = new ErpDbContext( + new DbContextOptionsBuilder().UseInMemoryDatabase(Guid.NewGuid().ToString("N"), Root).Options, + new TestCurrentUser(), new CurrentBranchContext(new TestCurrentUser())); + + db.Roles.AddRange( + new Role { RoleId = 1, Code = "Requester", Name = "Requester" }, + new Role { RoleId = ApproverRoleId, Code = "Approver", Name = "Approver" }, + new Role { RoleId = ClerkRoleId, Code = "Clerk", Name = "Clerk" }); + db.Users.AddRange( + new User { UserId = ERPCore.Domain.Entities.User.SystemUserId, Username = "system", DisplayName = "System" }, + new User { UserId = Requester, Username = "req", DisplayName = "Requester", RoleId = 1 }, + new User { UserId = ApproverA, Username = "a", DisplayName = "Approver A", RoleId = ApproverRoleId }, + new User { UserId = ApproverB, Username = "b", DisplayName = "Approver B", RoleId = ApproverRoleId }, + new User { UserId = Clerk, Username = "c", DisplayName = "Clerk", RoleId = ClerkRoleId }); + db.Permissions.AddRange( + new Permission { PermissionId = 45, Code = "NAV:stock.adjustments" }, + new Permission { PermissionId = 58, Code = "NAV:products.price-adjustments" }); + foreach (var roleId in new[] { ApproverRoleId, ClerkRoleId }) + foreach (var permissionId in new[] { 45, 58 }) + db.RolePermissions.Add(new RolePermission { RoleId = roleId, PermissionId = permissionId, Actions = RolePermissionAction.All }); + db.RolePermissions.Add(new RolePermission { RoleId = 1, PermissionId = 45, Actions = RolePermissionAction.View | RolePermissionAction.Create }); + + db.Branches.Add(new Branch { BranchId = 1, Code = "BR-001", Name = "Main" }); + db.Warehouses.Add(new Warehouse { WarehouseId = WarehouseId, Code = "WH-1", Name = "Main Store", BranchId = 1 }); + db.Uoms.Add(new Uom { UomId = 1, Name = "pcs" }); + db.Items.Add(new Item { ItemId = ItemId, Sku = "SKU-1", Name = "Cola", BaseUomId = 1, CategoryId = 1, + SellingMinPrice = 90m, SellingMaxPrice = 150m, SellingFlatPrice = 100m }); + db.ReasonCodes.Add(new ReasonCode { ReasonCodeId = ReasonId, Code = "DMG", Description = "Damage", Context = ReasonContext.Adjustment }); + db.ApprovalConfig.Add(new ApprovalConfig + { + ApprovalConfigId = ApprovalConfig.SingletonId, StockAdjustmentLevels = stockLevels, + StockAdjustmentLevel2ThresholdValue = stockThreshold, PriceAdjustmentLevels = 2, Level2RoleCodes = level2Roles + }); + await db.SaveChangesAsync(); + + var user = new SwitchableUser(Requester, "Requester"); + var uow = new PassThroughUnitOfWork(db); + var notifications = new RecordingNotifications(); + var approvals = new ApprovalService( + new Repository(db), new Repository(db), new Repository(db), + new Repository(db), user, notifications, uow); + var gl = new FakeGeneralLedger(); + var mutator = new RecordingMutator(); + var numbers = new FakeNumberSequence(); + + var stock = new AdjustmentService( + new Repository(db), new Repository(db), new Repository(db), + new Repository(db), new Repository(db), mutator, new FixedValuation(), + numbers, user, new FakeBranchConfig(), notifications, approvals, uow, db, gl, + new ConfigurationBuilder().Build(), + new SerialTrackingService(new Repository(db), new Repository(db), new Repository(db), + new Repository(db), user)); + var prices = new PriceAdjustmentService( + new Repository(db), new Repository(db), new Repository(db), + new Repository(db), new Repository(db), approvals, notifications, numbers, user, uow); + + return new Harness { Db = db, User = user, Mutator = mutator, Gl = gl, Notifications = notifications, Stock = stock, Prices = prices }; + } + } + + /// A signed-in user whose identity the test switches between steps. + private sealed class SwitchableUser(int userId, string roleCode) : ICurrentUser + { + private static readonly Dictionary Roles = new() + { + [Requester] = "Requester", [ApproverA] = "Approver", [ApproverB] = "Approver", [Clerk] = "Clerk" + }; + + public int AuditUserId { get; private set; } = userId; + public string? RoleCode { get; private set; } = roleCode; + public string UserId => AuditUserId.ToString(); + public bool IsAuthenticated => true; + public bool IsAdmin => false; + public int? BranchId => null; + + public void Be(int userId) + { + AuditUserId = userId; + RoleCode = Roles[userId]; + } + } + + /// Records what would be posted to stock; every unit is worth 10.00. + private sealed class RecordingMutator : IStockMutator + { + public List> Calls { get; } = new(); + + public Task> ApplyAsync(int warehouseId, string sourceDocType, int sourceDocId, DateTime now, + IReadOnlyList deltas, CancellationToken ct = default) + { + Calls.Add(deltas); + IReadOnlyList refs = deltas.Select(d => new StockLedger + { + ItemId = d.ItemId, WarehouseId = warehouseId, QtyBase = Math.Abs(d.QtyDelta), UnitCost = 10m, Value = Math.Abs(d.QtyDelta) * 10m, + Direction = d.QtyDelta < 0 ? Direction.Out : Direction.In, SourceDocType = sourceDocType, SourceDocId = sourceDocId, CreatedAt = now + }).ToList(); + return Task.FromResult(refs); + } + } + + /// 10 units on hand worth 100.00 — 10.00 each, which prices requests for the threshold. + private sealed class FixedValuation : IFifoCostingService + { + public Task GetValuationAsync(int itemId, int warehouseId, CancellationToken ct = default) + => Task.FromResult(new StockValuationDto(itemId, warehouseId, [], 10m, 100m, "LKR", "FIFO")); + public Task CreateInboundLayerAsync(int itemId, int warehouseId, int? batchId, int? serialId, int? grnLineId, + decimal qtyBase, decimal unitCost, DateTime receiptDate, CancellationToken ct = default) => throw new NotSupportedException(); + public Task> ConsumeAsync(int itemId, int warehouseId, int? batchId, decimal qtyBase, CancellationToken ct = default, + bool allowShortfall = false, IReadOnlyCollection? serialIds = null) => throw new NotSupportedException(); + public Task> SuggestBatchesAsync(int itemId, int warehouseId, decimal qtyBase, CancellationToken ct = default) + => throw new NotSupportedException(); + public Task PostLedgerAsync(int itemId, int warehouseId, int? binId, int? batchId, int? serialId, int userId, + Direction direction, decimal qtyBase, decimal unitCost, decimal runningBalance, string sourceDocType, int sourceDocId, + DateTime createdAt, CancellationToken ct = default, decimal? valueOverride = null) => throw new NotSupportedException(); + public Task GetOnHandAsync(int itemId, int warehouseId, CancellationToken ct = default) => Task.FromResult(10m); + } + + private sealed class RecordingNotifications : INotificationService + { + public List<(IReadOnlyList UserIds, string ModuleKey, string Message)> Targeted { get; } = new(); + + public Task RaiseAsync(string moduleKey, string title, string message, string? relatedEntityType = null, int? relatedEntityId = null, + CancellationToken ct = default) => Task.CompletedTask; + + public Task NotifyUsersAsync(IEnumerable userIds, string moduleKey, string title, string message, + string? relatedEntityType = null, int? relatedEntityId = null, CancellationToken ct = default) + { + Targeted.Add((userIds.ToList(), moduleKey, message)); + return Task.CompletedTask; + } + + public Task GetFeedAsync(int userId, int take = 20, CancellationToken ct = default) => throw new NotSupportedException(); + public Task MarkAllReadAsync(int userId, CancellationToken ct = default) => throw new NotSupportedException(); + } +} diff --git a/Backend/ERPCore.Tests/LowStockAlertServiceTests.cs b/Backend/ERPCore.Tests/LowStockAlertServiceTests.cs new file mode 100644 index 0000000..9360a77 --- /dev/null +++ b/Backend/ERPCore.Tests/LowStockAlertServiceTests.cs @@ -0,0 +1,116 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Common; +using ERPCore.Dtos.Notifications; +using ERPCore.Dtos.Stock; +using ERPCore.Infra.Persistence; +using ERPCore.Repositories; +using ERPCore.Services.Interfaces; +using ERPCore.Services.Stock; +using ERPCore.Tests.TestSupport; + +namespace ERPCore.Tests; + +/// Low-stock / reorder alerts raised after stock issues. +public class LowStockAlertServiceTests +{ + private const int ItemId = 1, WarehouseId = 1; + + [Theory] + [InlineData(12, 4, 8, true)] // 12 → 8 crosses the alert level of 10 + [InlineData(12, 2, 10, true)] // landing exactly on the level counts + [InlineData(20, 5, 15, false)] // still above the level + [InlineData(9, 3, 6, false)] // was already low — no repeat alert on every sale + public async Task Alerts_OnlyWhenAnIssueCrossesTheReorderPoint(decimal before, decimal issued, decimal after, bool alerts) + { + Assert.Equal(before - issued, after); + await using var db = Seed(); + var tracker = new LowStockTracker(); + tracker.Track(ItemId, WarehouseId, issued); + var notifications = new RecordingNotifications(); + + await Service(db, tracker, after, notifications).CheckAsync(); + + if (!alerts) + { + Assert.Empty(notifications.Raised); + return; + } + var raised = Assert.Single(notifications.Raised); + Assert.Equal(LowStockAlertService.ModuleKey, raised.ModuleKey); + Assert.Contains("SKU-1", raised.Message); + Assert.Contains($"down to {after:0.####} pcs at WH-1", raised.Message); + Assert.Contains("Suggested reorder: 50 pcs", raised.Message); + } + + [Fact] + public async Task ItemsWithoutARule_OrUntouched_AreIgnored() + { + await using var db = Seed(); + var tracker = new LowStockTracker(); + tracker.Track(itemId: 99, WarehouseId, 100); // no rule for this item + var notifications = new RecordingNotifications(); + + await Service(db, tracker, available: 0, notifications).CheckAsync(); + await Service(db, new LowStockTracker(), available: 0, notifications).CheckAsync(); // nothing issued + + Assert.Empty(notifications.Raised); + } + + [Fact] + public async Task Tracker_SumsIssuesWithinARequest_AndEmptiesWhenDrained() + { + var tracker = new LowStockTracker(); + tracker.Track(ItemId, WarehouseId, 3); + tracker.Track(ItemId, WarehouseId, 4); + tracker.Track(ItemId, WarehouseId, 0); + + Assert.Equal(7m, tracker.Drain()[(ItemId, WarehouseId)]); + Assert.Empty(tracker.Drain()); + } + + private static LowStockAlertService Service(ErpDbContext db, LowStockTracker tracker, decimal available, INotificationService notifications) + => new(tracker, new Repository(db), new FixedStock(available), notifications); + + private static ErpDbContext Seed() + { + var db = BankDepositTestSupport.CreateContext(); // users, branches, warehouses WH-1 / WH-2 + db.Uoms.Add(new Uom { UomId = 1, Name = "pcs" }); + db.Items.Add(new Item { ItemId = ItemId, Sku = "SKU-1", Name = "Cola 500ml", BaseUomId = 1, CategoryId = 1 }); + db.ItemReorders.Add(new ItemReorder { ReorderId = 1, ItemId = ItemId, WarehouseId = WarehouseId, ReorderPoint = 10, ReorderQty = 50 }); + db.SaveChanges(); + return db; + } + + private sealed class FixedStock(decimal available) : IStockService + { + public Task GetOnHandAsync(int itemId, int warehouseId, CancellationToken ct = default) + => Task.FromResult(new StockOnHandDto(itemId, warehouseId, available, available, 0, 0, 0, DateTime.UtcNow)); + public Task> GetOnHandListAsync(int? itemId, int? warehouseId, int? branchId, PageQuery query, CancellationToken ct = default) + => throw new NotSupportedException(); + public Task> GetAvailableByItemAsync(int warehouseId, CancellationToken ct = default) + => throw new NotSupportedException(); + public Task> GetLedgerAsync(int? itemId, int? warehouseId, int? branchId, DateOnly? from, DateOnly? to, + string? sourceDocType, int? sourceDocId, PageQuery query, CancellationToken ct = default) => throw new NotSupportedException(); + public Task GetValuationAsync(int itemId, int warehouseId, CancellationToken ct = default) + => throw new NotSupportedException(); + } + + private sealed class RecordingNotifications : INotificationService + { + public List<(string ModuleKey, string Title, string Message)> Raised { get; } = new(); + + public Task RaiseAsync(string moduleKey, string title, string message, string? relatedEntityType = null, + int? relatedEntityId = null, CancellationToken ct = default) + { + Raised.Add((moduleKey, title, message)); + return Task.CompletedTask; + } + + public Task NotifyUsersAsync(IEnumerable userIds, string moduleKey, string title, string message, + string? relatedEntityType = null, int? relatedEntityId = null, CancellationToken ct = default) => throw new NotSupportedException(); + + public Task GetFeedAsync(int userId, int take = 20, CancellationToken ct = default) => throw new NotSupportedException(); + public Task MarkAllReadAsync(int userId, CancellationToken ct = default) => throw new NotSupportedException(); + } +} diff --git a/Backend/ERPCore.Tests/TestSupport/BankDepositTestSupport.cs b/Backend/ERPCore.Tests/TestSupport/BankDepositTestSupport.cs new file mode 100644 index 0000000..6cc9501 --- /dev/null +++ b/Backend/ERPCore.Tests/TestSupport/BankDepositTestSupport.cs @@ -0,0 +1,168 @@ +using ERPCore.Common.Http; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Config; +using ERPCore.Infra.Auth; +using ERPCore.Infra.Gl; +using ERPCore.Infra.Persistence; +using ERPCore.Infra.UoW; +using ERPCore.Repositories; +using ERPCore.Services; +using ERPCore.Services.Gl; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.Configuration; + +namespace ERPCore.Tests.TestSupport; + +/// Records every journal it is asked to post; can be told to reject them instead. +internal sealed class FakeGeneralLedger : IGeneralLedgerService +{ + public const long CashAccountId = 1, BankAccountId = 2, OtherBankAccountId = 3; + + public List Posted { get; } = new(); + public bool Reject { get; set; } + + public IReadOnlyList Accounts { get; init; } = + [ + new("Cash", CashAccountId, "Main Till", null, "Petty", null, 101, "1000-01", "LKR"), + new("Bank", BankAccountId, "BOC Current", "Bank of Ceylon", null, "0001234567", 102, "1010-01", "LKR"), + new("Bank", OtherBankAccountId, "HNB Savings", "Hatton National Bank", null, "9876543210", 103, "1010-02", "LKR") + ]; + + public Task ForwardAsync(HttpMethod method, string path, string? queryString, string? contentType, Stream? body, CancellationToken ct) + => throw new NotSupportedException(); + + public Task PostJournalEntryAsync(GlJournalEntryRequest request, CancellationToken ct) + { + if (Reject) throw new DomainException(ErrorCodes.Validation, "GL rejected the entry.", 502); + Posted.Add(request); + return Task.FromResult(new GlJournalEntryResult($"JE-{Posted.Count}", true)); + } + + public Task GetPeriodByDateAsync(DateOnly date, CancellationToken ct) => Task.FromResult(new GlPeriod(1, 1)); + + public Task> ListBankAccountsAsync(CancellationToken ct) => Task.FromResult(Accounts); +} + +/// The in-memory provider has no transactions: run the work, then save — what the real one commits. +internal sealed class PassThroughUnitOfWork(ErpDbContext db) : IUnitOfWork +{ + public Task SaveChangesAsync(CancellationToken ct = default) => db.SaveChangesAsync(ct); + + public async Task ExecuteInTransactionAsync(Func action, CancellationToken ct = default) + { + await action(ct); + await db.SaveChangesAsync(ct); + } + + public async Task ExecuteInTransactionAsync(Func> action, CancellationToken ct = default) + { + var result = await action(ct); + await db.SaveChangesAsync(ct); + return result; + } +} + +internal sealed class FakeNumberSequence : INumberSequenceService +{ + private int _next; + public Task NextAsync(string docType, int branchId, CancellationToken ct = default) => Task.FromResult($"{docType}-{++_next:00000}"); + public Task NextAsync(string docType, CancellationToken ct = default) => Task.FromResult($"{docType}-{++_next:00000}"); +} + +/// Branching on: the requested branch is used as-is. +internal sealed class FakeBranchConfig : IBranchConfigService +{ + public Task> GetAsync(CancellationToken ct = default) => throw new NotSupportedException(); + public Task> UpdateAsync(UpdateBranchConfigRequest request, uint expectedRowVersion, CancellationToken ct = default) + => throw new NotSupportedException(); + public Task IsBranchingEnabledAsync(CancellationToken ct = default) => Task.FromResult(true); + public Task ResolveBranchIdAsync(int requestedBranchId, CancellationToken ct = default) + => requestedBranchId > 0 ? Task.FromResult(requestedBranchId) : throw new DomainException(ErrorCodes.Validation, "Branch is required.", 422); + public Task IsWarehouseBranchingEnabledAsync(CancellationToken ct = default) => Task.FromResult(true); + public Task ResolveWarehouseBranchIdAsync(int requestedBranchId, CancellationToken ct = default) => Task.FromResult(requestedBranchId); + public Task ResolveDefaultBranchIdAsync(CancellationToken ct = default) => Task.FromResult(1); +} + +/// Seeds and services for the cash-in-hand / bank-deposit tests. Two branches, one warehouse each. +internal static class BankDepositTestSupport +{ + private static readonly InMemoryDatabaseRoot Root = new(); + + public const int Branch1 = 1, Branch2 = 2; + public const int Warehouse1 = 1, Warehouse2 = 2; + + public static BusinessDayClock Clock { get; } = new(Config()); + + public static IConfiguration Config(DateOnly? trackingStart = null) => new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["DayEndReport:TimeZone"] = "Asia/Colombo", + ["BankDeposit:DefaultCashAccountCode"] = "1000-01", + ["BankDeposit:CashTrackingStartDate"] = trackingStart?.ToString("yyyy-MM-dd") + }) + .Build(); + + /// Midday on a local business day, as the UTC instant documents are stamped with. + public static DateTime At(DateOnly day) => Clock.ToUtc(day).AddHours(6); + + public static ErpDbContext CreateContext() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(Guid.NewGuid().ToString("N"), Root) + .Options; + var currentUser = new TestCurrentUser(); + var db = new ErpDbContext(options, currentUser, new CurrentBranchContext(currentUser)); + + db.Users.Add(new User { UserId = User.SystemUserId, Username = "system", DisplayName = "System" }); + db.Branches.AddRange( + new Branch { BranchId = Branch1, Code = "BR-001", Name = "Colombo" }, + new Branch { BranchId = Branch2, Code = "BR-002", Name = "Kandy" }); + db.Warehouses.AddRange( + new Warehouse { WarehouseId = Warehouse1, Code = "WH-1", Name = "Colombo Store", BranchId = Branch1 }, + new Warehouse { WarehouseId = Warehouse2, Code = "WH-2", Name = "Kandy Store", BranchId = Branch2 }); + db.SaveChanges(); + return db; + } + + private static int _id; + + public static SalesSlip CashSlip(DateOnly day, decimal cash, decimal card = 0m, int warehouseId = Warehouse1) + { + var id = Interlocked.Increment(ref _id); + return new SalesSlip + { + SalesSlipId = id, SlipNo = $"SL-{id}", SlipDate = At(day), Status = SalesSlipStatus.Posted, WarehouseId = warehouseId, + GrandTotal = cash + card, PaidAmount = cash + card, CashAmount = cash, CardAmount = card + }; + } + + public static CashInHandService CashService(ErpDbContext db, DateOnly? trackingStart) + => new(new Repository(db), new Repository(db), new Repository(db), + new Repository(db), new Repository(db), + new CurrentBranchContext(new TestCurrentUser()), Clock, Config(trackingStart)); + + public static BankDepositService DepositService(ErpDbContext db, DateOnly? trackingStart, FakeGeneralLedger gl) + { + var currentUser = new TestCurrentUser(); + return new BankDepositService( + new Repository(db), new Repository(db), CashService(db, trackingStart), gl, + currentUser, new CurrentBranchContext(currentUser), new FakeNumberSequence(), new FakeBranchConfig(), + new PassThroughUnitOfWork(db), db, Clock); + } + + public static BankDeposit Deposit(DateOnly businessDate, decimal amount, int branchId = Branch1, + BankDepositStatus status = BankDepositStatus.Posted) + { + var id = Interlocked.Increment(ref _id); + return new BankDeposit + { + BankDepositId = id, DocNo = $"BD-{id}", BranchId = branchId, BusinessDate = businessDate, DepositDate = businessDate, + Amount = amount, Status = status, FromCashAccountCode = "1000-01", FromCashAccountName = "Main Till", + ToBankAccountCode = "1010-01", ToBankAccountName = "BOC Current", CreatedBy = User.SystemUserId + }; + } +} diff --git a/Backend/ERPCore/Controllers/ApprovalsController.cs b/Backend/ERPCore/Controllers/ApprovalsController.cs new file mode 100644 index 0000000..f3d691a --- /dev/null +++ b/Backend/ERPCore/Controllers/ApprovalsController.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Authorization; +using ERPCore.Dtos.Approvals; +using ERPCore.Infra.Auth; +using ERPCore.Services; +using ERPCore.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ERPCore.Controllers; + +/// +/// Approvals inbox and settings (docs/DUAL_APPROVAL_PLAN.md §7, §10). The inbox is open to every +/// signed-in user — it only lists what they may approve and their own requests. Reading the +/// settings is open too (screens need to know whether prices are locked); changing them needs +/// settings.approvals. +/// +[Route("api/v1/approvals")] +public sealed class ApprovalsController : ApiControllerBase +{ + private readonly ApprovalInboxService _inbox; + private readonly IApprovalService _approvals; + + public ApprovalsController(ApprovalInboxService inbox, IApprovalService approvals) + { + _inbox = inbox; + _approvals = approvals; + } + + [HttpGet("pending")] + [ProducesResponseType(typeof(PendingApprovalsDto), StatusCodes.Status200OK)] + public async Task> Pending(CancellationToken ct) + => Ok(await _inbox.PendingAsync(ct)); + + [HttpGet("config")] + [ProducesResponseType(typeof(ApprovalConfigDto), StatusCodes.Status200OK)] + public async Task> GetConfig(CancellationToken ct) + => Ok(await _approvals.GetConfigAsync(ct)); + + [HttpPut("config")] + [Authorize(Policy = NavPolicies.Prefix + "settings.approvals")] + [ProducesResponseType(typeof(ApprovalConfigDto), StatusCodes.Status200OK)] + public async Task> UpdateConfig([FromBody] UpdateApprovalConfigRequest request, CancellationToken ct) + => Ok(await _approvals.UpdateConfigAsync(request, ct)); +} diff --git a/Backend/ERPCore/Controllers/BankDepositsController.cs b/Backend/ERPCore/Controllers/BankDepositsController.cs new file mode 100644 index 0000000..8c25e40 --- /dev/null +++ b/Backend/ERPCore/Controllers/BankDepositsController.cs @@ -0,0 +1,78 @@ +using Microsoft.AspNetCore.Authorization; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Accounts; +using ERPCore.Dtos.Common; +using ERPCore.Infra.Auth; +using ERPCore.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ERPCore.Controllers; + +/// +/// Till cash deposited to the bank, and the cash in hand behind it (docs/BANK_DEPOSIT_MODULE_PLAN.md §7.3). +/// Recording needs the Create action on accounts.bank-deposits; cancelling needs Delete. +/// +[Route("api/v1/bank-deposits")] +[Authorize(Policy = NavPolicies.Prefix + "accounts.bank-deposits")] +public sealed class BankDepositsController : ApiControllerBase +{ + private readonly IBankDepositService _deposits; + private readonly ICashInHandService _cash; + + public BankDepositsController(IBankDepositService deposits, ICashInHandService cash) + { + _deposits = deposits; + _cash = cash; + } + + /// Deposits filtered by business date range, branch, bank account and status, plus their total. + [HttpGet] + [ProducesResponseType(typeof(BankDepositListDto), StatusCodes.Status200OK)] + public async Task> List( + [FromQuery] PageQuery query, [FromQuery] DateOnly? from, [FromQuery] DateOnly? to, [FromQuery] int? branchId, + [FromQuery] long? bankAccountId, [FromQuery] BankDepositStatus? status, CancellationToken ct) + => Ok(await _deposits.ListAsync(query, new BankDepositListFilter(from, to, branchId, bankAccountId, status), ct)); + + [HttpGet("{bankDepositId:int}")] + [ProducesResponseType(typeof(BankDepositDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> GetById(int bankDepositId, CancellationToken ct) + { + var result = await _deposits.GetAsync(bankDepositId, ct); + return result is null ? NotFound() : Ok(result); + } + + /// One business day's cash position (defaults to today) and how much of it can still be deposited. + [HttpGet("cash-in-hand")] + [ProducesResponseType(typeof(CashInHandDto), StatusCodes.Status200OK)] + public async Task> CashInHand([FromQuery] DateOnly? date, [FromQuery] int? branchId, CancellationToken ct) + => Ok(await _cash.GetPositionAsync(date, branchId, ct)); + + /// Day-wise cash position for from..to inclusive (at most 92 days). + [HttpGet("cash-in-hand/daily")] + [ProducesResponseType(typeof(CashInHandDailyDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task> CashInHandDaily( + [FromQuery] DateOnly from, [FromQuery] DateOnly to, [FromQuery] int? branchId, CancellationToken ct) + => Ok(await _cash.GetDailyAsync(from, to, branchId, ct)); + + /// Record a deposit; posts GL (Debit bank / Credit cash). Rejected above the cash in hand. + [HttpPost] + [Authorize(Policy = NavPolicies.Prefix + "accounts.bank-deposits" + NavPolicies.ActionSeparator + NavPolicies.CreateAction)] + [ProducesResponseType(typeof(BankDepositDto), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task> Create([FromBody] CreateBankDepositRequest request, CancellationToken ct) + { + var result = await _deposits.CreateAsync(request, ct); + return Created($"/api/v1/bank-deposits/{result.BankDepositId}", result); + } + + /// Cancel a Posted deposit; posts the reversing GL entry and puts the cash back in hand. + [HttpPost("{bankDepositId:int}/cancel")] + [Authorize(Policy = NavPolicies.Prefix + "accounts.bank-deposits" + NavPolicies.ActionSeparator + NavPolicies.DeleteAction)] + [ProducesResponseType(typeof(BankDepositDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Cancel(int bankDepositId, [FromBody] CancelBankDepositRequest request, CancellationToken ct) + => Ok(await _deposits.CancelAsync(bankDepositId, request, ct)); +} diff --git a/Backend/ERPCore/Controllers/DayEndReportController.cs b/Backend/ERPCore/Controllers/DayEndReportController.cs new file mode 100644 index 0000000..590a239 --- /dev/null +++ b/Backend/ERPCore/Controllers/DayEndReportController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Authorization; +using ERPCore.Dtos.Reports; +using ERPCore.Infra.Auth; +using ERPCore.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ERPCore.Controllers; + +/// Day-End Sales Report — a day's sales, payments by method, expenses and income, and the +/// same day-wise over a range for the income chart/PDF (docs/DAY_END_SALES_REPORT_PLAN.md §4). +[Route("api/v1/day-end-report")] +[Authorize(Policy = NavPolicies.Prefix + "sales.day-end-report")] +public sealed class DayEndReportController : ApiControllerBase +{ + private readonly IDayEndReportService _reports; + + public DayEndReportController(IDayEndReportService reports) => _reports = reports; + + /// One business day (defaults to today in the report's time zone). + [HttpGet] + [ProducesResponseType(typeof(DayEndReportDto), StatusCodes.Status200OK)] + public async Task> GetDay([FromQuery] DateOnly? date, [FromQuery] int? branchId, CancellationToken ct) + => Ok(await _reports.GetDayAsync(date, branchId, ct)); + + /// Day-wise figures for from..to inclusive (at most 92 days). + [HttpGet("daily")] + [ProducesResponseType(typeof(DayEndDailyReportDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task> GetDaily([FromQuery] DateOnly from, [FromQuery] DateOnly to, [FromQuery] int? branchId, CancellationToken ct) + => Ok(await _reports.GetDailyAsync(from, to, branchId, ct)); +} diff --git a/Backend/ERPCore/Controllers/ExpenseSetupController.cs b/Backend/ERPCore/Controllers/ExpenseSetupController.cs new file mode 100644 index 0000000..bd6fea8 --- /dev/null +++ b/Backend/ERPCore/Controllers/ExpenseSetupController.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Authorization; +using ERPCore.Dtos.Expenses; +using ERPCore.Infra.Auth; +using ERPCore.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ERPCore.Controllers; + +/// +/// Expense categories and titles (docs/EXPENSE_MODULE_PLAN.md §6 "Setup"). Reading is open to +/// anyone with Accounts access (the expense form needs the lists); changes need the +/// Expense Setup permission. Categories are the fixed seeded four — there is no create +/// endpoint; only their active flag can change. Titles can be added under them. +/// +[Route("api/v1")] +[Authorize(Policy = NavPolicies.Prefix + "accounts")] +public sealed class ExpenseSetupController : ApiControllerBase +{ + private const string SetupPolicy = NavPolicies.Prefix + "accounts.expense-setup"; + + private readonly IExpenseSetupService _setup; + + public ExpenseSetupController(IExpenseSetupService setup) => _setup = setup; + + /// Categories, each with its titles, in display order. + [HttpGet("expense-categories")] + [ProducesResponseType(typeof(IReadOnlyList), StatusCodes.Status200OK)] + public async Task>> List([FromQuery] bool includeInactive, CancellationToken ct) + => Ok(await _setup.ListAsync(includeInactive, ct)); + + [HttpPut("expense-categories/{expenseCategoryId:int}")] + [Authorize(Policy = SetupPolicy)] + [ProducesResponseType(typeof(ExpenseCategoryDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> UpdateCategory(int expenseCategoryId, [FromBody] UpdateExpenseCategoryRequest request, CancellationToken ct) + => Ok(await _setup.UpdateCategoryAsync(expenseCategoryId, request, ct)); + + [HttpPost("expense-categories/{expenseCategoryId:int}/titles")] + [Authorize(Policy = SetupPolicy)] + [ProducesResponseType(typeof(ExpenseTitleDto), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> CreateTitle(int expenseCategoryId, [FromBody] CreateExpenseTitleRequest request, CancellationToken ct) + { + var dto = await _setup.CreateTitleAsync(expenseCategoryId, request, ct); + return Created($"/api/v1/expense-titles/{dto.ExpenseTitleId}", dto); + } + + [HttpPut("expense-titles/{expenseTitleId:int}")] + [Authorize(Policy = SetupPolicy)] + [ProducesResponseType(typeof(ExpenseTitleDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> UpdateTitle(int expenseTitleId, [FromBody] UpdateExpenseTitleRequest request, CancellationToken ct) + => Ok(await _setup.UpdateTitleAsync(expenseTitleId, request, ct)); +} diff --git a/Backend/ERPCore/Controllers/ExpensesController.cs b/Backend/ERPCore/Controllers/ExpensesController.cs new file mode 100644 index 0000000..0488524 --- /dev/null +++ b/Backend/ERPCore/Controllers/ExpensesController.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Authorization; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Common; +using ERPCore.Dtos.Expenses; +using ERPCore.Infra.Auth; +using ERPCore.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ERPCore.Controllers; + +/// Business expense bills — draft, post (GL accrual), pay, cancel (docs/EXPENSE_MODULE_PLAN.md §6). +[Route("api/v1/expenses")] +[Authorize(Policy = NavPolicies.Prefix + "accounts.expenses")] +public sealed class ExpensesController : ApiControllerBase +{ + private readonly IExpenseService _expenses; + private readonly IExpensePaymentService _payments; + + public ExpensesController(IExpenseService expenses, IExpensePaymentService payments) + { + _expenses = expenses; + _payments = payments; + } + + [HttpGet] + [ProducesResponseType(typeof(PagedResponse), StatusCodes.Status200OK)] + public async Task>> List( + [FromQuery] PageQuery query, [FromQuery] ExpenseStatus? status, [FromQuery] ExpensePaymentStatus? paymentStatus, + [FromQuery] int? categoryId, [FromQuery] int? titleId, [FromQuery] DateOnly? from, [FromQuery] DateOnly? to, + [FromQuery] bool overdueOnly, CancellationToken ct) + => Ok(await _expenses.ListAsync(query, new ExpenseListFilter(status, paymentStatus, categoryId, titleId, from, to, overdueOnly), ct)); + + /// Totals by category → title for Posted expenses dated in [from, to] (default: this month), plus unpaid/overdue position. + [HttpGet("summary")] + [ProducesResponseType(typeof(ExpenseSummaryReportDto), StatusCodes.Status200OK)] + public async Task> Summary([FromQuery] DateOnly? from, [FromQuery] DateOnly? to, CancellationToken ct) + => Ok(await _expenses.GetSummaryAsync(from, to, ct)); + + [HttpGet("{expenseId:int}")] + [ProducesResponseType(typeof(ExpenseDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> GetById(int expenseId, CancellationToken ct) + { + var result = await _expenses.GetAsync(expenseId, ct); + if (result is null) return NotFound(); + SetETag(result.RowVersion); + return Ok(result.Value); + } + + [HttpPost] + [ProducesResponseType(typeof(ExpenseDto), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] + public async Task> Create([FromBody] CreateExpenseRequest request, CancellationToken ct) + { + var result = await _expenses.CreateAsync(request, ct); + SetETag(result.RowVersion); + return Created($"/api/v1/expenses/{result.Value.ExpenseId}", result.Value); + } + + [HttpPut("{expenseId:int}")] + [ProducesResponseType(typeof(ExpenseDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + [ProducesResponseType(StatusCodes.Status412PreconditionFailed)] + public async Task> Update(int expenseId, [FromBody] UpdateExpenseRequest request, CancellationToken ct) + { + var expected = RequireIfMatch(); + var result = await _expenses.UpdateAsync(expenseId, request, expected, ct); + SetETag(result.RowVersion); + return Ok(result.Value); + } + + /// Draft only. + [HttpDelete("{expenseId:int}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task Delete(int expenseId, CancellationToken ct) + { + await _expenses.DeleteAsync(expenseId, ct); + return NoContent(); + } + + /// Accrue the expense to GL (Debit expense account / Credit Accrued Expenses). + [HttpPost("{expenseId:int}/post")] + [ProducesResponseType(typeof(ExpenseDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Post(int expenseId, CancellationToken ct) + => Ok(await _expenses.PostAsync(expenseId, ct)); + + /// Post a Draft and pay it, in full or partially, in one transaction; any remainder stays as the balance. + [HttpPost("{expenseId:int}/post-and-pay")] + [ProducesResponseType(typeof(ExpenseDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> PostAndPay(int expenseId, [FromBody] CreateExpensePaymentRequest request, CancellationToken ct) + => Ok(await _payments.PostAndPayAsync(expenseId, request, ct)); + + /// Draft, or Posted with no payments (posts the reversing GL entry). + [HttpPost("{expenseId:int}/cancel")] + [ProducesResponseType(typeof(ExpenseDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Cancel(int expenseId, [FromBody] CancelExpenseRequest request, CancellationToken ct) + => Ok(await _expenses.CancelAsync(expenseId, request, ct)); + + /// Pay against the balance, in full or in installments; one GL entry per submission. + [HttpPost("{expenseId:int}/payments")] + [ProducesResponseType(typeof(IReadOnlyList), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task>> Pay(int expenseId, [FromBody] CreateExpensePaymentRequest request, CancellationToken ct) + { + var rows = await _payments.PayAsync(expenseId, request, ct); + return Created($"/api/v1/expenses/{expenseId}/payments", rows); + } + + /// Payment history, newest first. + [HttpGet("{expenseId:int}/payments")] + [ProducesResponseType(typeof(IReadOnlyList), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task>> ListPayments(int expenseId, CancellationToken ct) + => Ok(await _payments.ListAsync(expenseId, ct)); +} diff --git a/Backend/ERPCore/Controllers/ItemsController.cs b/Backend/ERPCore/Controllers/ItemsController.cs index b420a19..8f3dbe2 100644 --- a/Backend/ERPCore/Controllers/ItemsController.cs +++ b/Backend/ERPCore/Controllers/ItemsController.cs @@ -57,6 +57,13 @@ public sealed class ItemsController : ApiControllerBase return result is null ? NotFound() : Ok(result); } + /// The item's latest posted GRN receipts (default 5, newest first): unit cost, discount, net cost, vendor. + [HttpGet("{itemId:int}/purchase-prices")] + [ProducesResponseType(typeof(IReadOnlyList), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task>> PurchasePrices(int itemId, [FromQuery] int take, CancellationToken ct) + => Ok(await _items.GetPurchasePricesAsync(itemId, take <= 0 ? 5 : take, ct)); + /// Get a single item; returns an ETag for optimistic concurrency. [HttpGet("{itemId:int}")] [ProducesResponseType(typeof(ItemDetailDto), StatusCodes.Status200OK)] diff --git a/Backend/ERPCore/Controllers/PriceAdjustmentsController.cs b/Backend/ERPCore/Controllers/PriceAdjustmentsController.cs new file mode 100644 index 0000000..0f9fe51 --- /dev/null +++ b/Backend/ERPCore/Controllers/PriceAdjustmentsController.cs @@ -0,0 +1,89 @@ +using Microsoft.AspNetCore.Authorization; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Approvals; +using ERPCore.Dtos.Common; +using ERPCore.Dtos.Products; +using ERPCore.Infra.Auth; +using ERPCore.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ERPCore.Controllers; + +/// +/// Selling-price change requests (docs/DUAL_APPROVAL_PLAN.md §6.4). Requesting needs Create on +/// products.price-adjustments, deciding needs Approve; an item's applied price history is +/// readable by anyone who can see Products. +/// +[Route("api/v1/price-adjustments")] +[Authorize(Policy = NavPolicies.Prefix + "products")] +public sealed class PriceAdjustmentsController : ApiControllerBase +{ + private const string Screen = NavPolicies.Prefix + "products.price-adjustments"; + private const string CreatePolicy = Screen + NavPolicies.ActionSeparator + NavPolicies.CreateAction; + private const string ApprovePolicy = Screen + NavPolicies.ActionSeparator + NavPolicies.ApproveAction; + + private readonly IPriceAdjustmentService _prices; + + public PriceAdjustmentsController(IPriceAdjustmentService prices) => _prices = prices; + + [HttpGet] + [Authorize(Policy = Screen)] + [ProducesResponseType(typeof(PagedResponse), StatusCodes.Status200OK)] + public async Task>> List( + [FromQuery] PageQuery query, [FromQuery] PriceAdjustmentStatus? status, [FromQuery] int? itemId, CancellationToken ct) + => Ok(await _prices.ListAsync(query, status, itemId, ct)); + + [HttpGet("{priceAdjustmentId:int}")] + [Authorize(Policy = Screen)] + [ProducesResponseType(typeof(PriceAdjustmentDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> GetById(int priceAdjustmentId, CancellationToken ct) + { + var dto = await _prices.GetAsync(priceAdjustmentId, ct); + return dto is null ? NotFound() : Ok(dto); + } + + /// Request new selling prices for one or more items. + [HttpPost] + [Authorize(Policy = CreatePolicy)] + [ProducesResponseType(typeof(PriceAdjustmentDto), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Create([FromBody] CreatePriceAdjustmentRequest request, CancellationToken ct) + { + var dto = await _prices.CreateAsync(request, ct); + return Created($"/api/v1/price-adjustments/{dto.PriceAdjustmentId}", dto); + } + + /// Give the next approval; the final one applies the prices. + [HttpPost("{priceAdjustmentId:int}/approve")] + [Authorize(Policy = ApprovePolicy)] + [ProducesResponseType(typeof(PriceAdjustmentDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Approve(int priceAdjustmentId, [FromBody] ApproveRequest? request, CancellationToken ct) + => Ok(await _prices.ApproveAsync(priceAdjustmentId, request?.Comment, ct)); + + [HttpPost("{priceAdjustmentId:int}/reject")] + [Authorize(Policy = ApprovePolicy)] + [ProducesResponseType(typeof(PriceAdjustmentDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Reject(int priceAdjustmentId, [FromBody] RejectRequest request, CancellationToken ct) + => Ok(await _prices.RejectAsync(priceAdjustmentId, request.Reason, ct)); + + /// The requester withdraws a pending request. + [HttpPost("{priceAdjustmentId:int}/cancel")] + [Authorize(Policy = Screen)] + [ProducesResponseType(typeof(PriceAdjustmentDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Cancel(int priceAdjustmentId, CancellationToken ct) + => Ok(await _prices.CancelAsync(priceAdjustmentId, ct)); + + /// Every applied price change for an item, newest first, with who approved it. + [HttpGet("/api/v1/items/{itemId:int}/price-history")] + [ProducesResponseType(typeof(IReadOnlyList), StatusCodes.Status200OK)] + public async Task>> ItemHistory(int itemId, CancellationToken ct) + => Ok(await _prices.ItemHistoryAsync(itemId, ct)); +} diff --git a/Backend/ERPCore/Controllers/StockAdjustmentsController.cs b/Backend/ERPCore/Controllers/StockAdjustmentsController.cs index 1b10b37..5f8e6e0 100644 --- a/Backend/ERPCore/Controllers/StockAdjustmentsController.cs +++ b/Backend/ERPCore/Controllers/StockAdjustmentsController.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Authorization; +using ERPCore.Domain.Enums; using ERPCore.Infra.Auth; +using ERPCore.Dtos.Approvals; using ERPCore.Dtos.Common; using ERPCore.Dtos.Stock; using ERPCore.Services.Interfaces; @@ -7,23 +9,30 @@ using Microsoft.AspNetCore.Mvc; namespace ERPCore.Controllers; -/// Stock-adjustment endpoints (docs/11 §5.5). +/// +/// Stock-adjustment endpoints (docs/11 §5.5). Creating submits a request; it moves stock once +/// approved (docs/DUAL_APPROVAL_PLAN.md §5.4). Approving or rejecting needs the Approve action on +/// stock.adjustments; the two-person rules are enforced by the service. +/// [Route("api/v1/stock-adjustments")] [Authorize(Policy = NavPolicies.Prefix + "stock")] public sealed class StockAdjustmentsController : ApiControllerBase { + private const string ApprovePolicy = NavPolicies.Prefix + "stock.adjustments" + NavPolicies.ActionSeparator + NavPolicies.ApproveAction; + private readonly IAdjustmentService _adjustments; public StockAdjustmentsController(IAdjustmentService adjustments) => _adjustments = adjustments; - /// List posted adjustments, newest first. + /// List adjustments (posted and requests), newest first. [HttpGet] [ProducesResponseType(typeof(PagedResponse), StatusCodes.Status200OK)] public async Task>> List( - [FromQuery] PageQuery query, [FromQuery] int? warehouseId, [FromQuery] int? branchId, [FromQuery] int? reasonCodeId, CancellationToken ct) - => Ok(await _adjustments.ListAsync(query, warehouseId, branchId, reasonCodeId, ct)); + [FromQuery] PageQuery query, [FromQuery] int? warehouseId, [FromQuery] int? branchId, [FromQuery] int? reasonCodeId, + [FromQuery] AdjustmentStatus? status, CancellationToken ct) + => Ok(await _adjustments.ListAsync(query, warehouseId, branchId, reasonCodeId, status, ct)); - /// Get one adjustment with its lines and the ledger entries it posted. + /// Get one adjustment with its lines, approval trail and the ledger entries it posted. [HttpGet("{adjustmentId:int}")] [ProducesResponseType(typeof(AdjustmentDto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -33,7 +42,7 @@ public sealed class StockAdjustmentsController : ApiControllerBase return dto is null ? NotFound() : Ok(dto); } - /// Create + auto-post an adjustment (mandatory reason code; decrease FIFO-consumes). + /// Submit an adjustment (mandatory reason code). Posts at once only when approvals are off. [HttpPost] [ProducesResponseType(typeof(AdjustmentDto), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -44,4 +53,29 @@ public sealed class StockAdjustmentsController : ApiControllerBase var dto = await _adjustments.CreateAsync(request, ct); return Created($"/api/v1/stock-adjustments/{dto.AdjustmentId}", dto); } + + /// Give the next approval. The final one posts the stock movement and GL. + [HttpPost("{adjustmentId:int}/approve")] + [Authorize(Policy = ApprovePolicy)] + [ProducesResponseType(typeof(AdjustmentDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Approve(int adjustmentId, [FromBody] ApproveRequest? request, CancellationToken ct) + => Ok(await _adjustments.ApproveAsync(adjustmentId, request?.Comment, ct)); + + [HttpPost("{adjustmentId:int}/reject")] + [Authorize(Policy = ApprovePolicy)] + [ProducesResponseType(typeof(AdjustmentDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Reject(int adjustmentId, [FromBody] RejectRequest request, CancellationToken ct) + => Ok(await _adjustments.RejectAsync(adjustmentId, request.Reason, ct)); + + /// The requester withdraws a pending request. + [HttpPost("{adjustmentId:int}/cancel")] + [ProducesResponseType(typeof(AdjustmentDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task> Cancel(int adjustmentId, CancellationToken ct) + => Ok(await _adjustments.CancelAsync(adjustmentId, ct)); } diff --git a/Backend/ERPCore/Domain/DocumentTypes.cs b/Backend/ERPCore/Domain/DocumentTypes.cs index f00ad99..1fe3fdb 100644 --- a/Backend/ERPCore/Domain/DocumentTypes.cs +++ b/Backend/ERPCore/Domain/DocumentTypes.cs @@ -26,4 +26,13 @@ public static class DocumentTypes /// Cashier day-end close-out (Sales) — DEND-2026-00001. public const string SalesDayEnd = "DEND"; + + /// Business expense bill (Accounts) — EXP-BR-001-2026-00001. + public const string Expense = "EXP"; + + /// Till cash deposited to a bank account (Accounts) — BD-BR-001-2026-00001. + public const string BankDeposit = "BD"; + + /// Selling-price change request (Products) — PADJ-2026-00001. + public const string PriceAdjustment = "PADJ"; } diff --git a/Backend/ERPCore/Domain/Entities/BankDeposit.cs b/Backend/ERPCore/Domain/Entities/BankDeposit.cs new file mode 100644 index 0000000..57e01a2 --- /dev/null +++ b/Backend/ERPCore/Domain/Entities/BankDeposit.cs @@ -0,0 +1,55 @@ +using ERPCore.Domain.Enums; + +namespace ERPCore.Domain.Entities; + +/// +/// Till cash taken to the bank (docs/BANK_DEPOSIT_MODULE_PLAN.md §4). Saving it posts a GL entry +/// (Debit the bank account / Credit the cash account) and lowers the branch's cash in hand for +/// . There is no draft: a wrong deposit is cancelled (the entry is +/// reversed) and entered again. GL accounts have no local FK, so the account details are snapshots. +/// +public class BankDeposit +{ + public int BankDepositId { get; set; } + public string DocNo { get; set; } = string.Empty; + + public int BranchId { get; set; } + public Branch? Branch { get; set; } + + /// The day whose cash is deposited — cash in hand and the day-end report group by this. + public DateOnly BusinessDate { get; set; } + /// The day the bank received it; the GL entry date. + public DateOnly DepositDate { get; set; } + + public decimal Amount { get; set; } + + public long FromGlCashAccountId { get; set; } + public string FromCashAccountCode { get; set; } = string.Empty; + public string FromCashAccountName { get; set; } = string.Empty; + + public long ToGlBankAccountId { get; set; } + public string ToBankAccountCode { get; set; } = string.Empty; + public string ToBankAccountName { get; set; } = string.Empty; + public string? ToBankName { get; set; } + public string? ToAccountNumber { get; set; } + + public string? DepositSlipNo { get; set; } + public string? DepositedBy { get; set; } + public string? Remarks { get; set; } + + public BankDepositStatus Status { get; set; } = BankDepositStatus.Posted; + + public string? GlJournalNo { get; set; } + public DateTime? GlPostedAt { get; set; } + + public string? GlReversalJournalNo { get; set; } + public string? CancelReason { get; set; } + public int? CancelledBy { get; set; } + public DateTime? CancelledAt { get; set; } + + public int CreatedBy { get; set; } + public User? Creator { get; set; } + public DateTime CreatedAt { get; set; } + + public uint RowVersion { get; set; } +} diff --git a/Backend/ERPCore/Domain/Entities/BundleSale.cs b/Backend/ERPCore/Domain/Entities/BundleSale.cs index c0109ac..a195f29 100644 --- a/Backend/ERPCore/Domain/Entities/BundleSale.cs +++ b/Backend/ERPCore/Domain/Entities/BundleSale.cs @@ -25,6 +25,11 @@ public class BundleSale public decimal DiscountTotal { get; set; } public decimal TaxTotal { get; set; } public decimal GrandTotal { get; set; } + + /// How the (always fully paid) was tendered: + /// CashAmount + CardAmount == GrandTotal. Backfilled as all cash. + public decimal CashAmount { get; set; } + public decimal CardAmount { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } public uint RowVersion { get; set; } diff --git a/Backend/ERPCore/Domain/Entities/DocumentApproval.cs b/Backend/ERPCore/Domain/Entities/DocumentApproval.cs new file mode 100644 index 0000000..eaffa73 --- /dev/null +++ b/Backend/ERPCore/Domain/Entities/DocumentApproval.cs @@ -0,0 +1,48 @@ +using ERPCore.Domain.Enums; + +namespace ERPCore.Domain.Entities; + +/// +/// One approval decision on a document (docs/DUAL_APPROVAL_PLAN.md §4.1) — a row per decision, so +/// the full trail survives. Polymorphic like the stock ledger: + +/// , no FK. Doc types: ADJ (stock adjustment), PADJ (price adjustment). +/// +public class DocumentApproval +{ + public int DocumentApprovalId { get; set; } + public string DocType { get; set; } = string.Empty; + public int DocId { get; set; } + /// 1 or 2. + public int Level { get; set; } + public ApprovalDecision Decision { get; set; } + public int DecidedBy { get; set; } + public User? Decider { get; set; } + public DateTime DecidedAt { get; set; } + public string? Comment { get; set; } +} + +/// +/// Approval rules (docs/DUAL_APPROVAL_PLAN.md §7). Singleton row, edited under Settings → Approvals. +/// Levels: 0 = no approval (applies on create), 1 = one approver, 2 = two different approvers. +/// +public class ApprovalConfig +{ + public const int SingletonId = 1; + + public int ApprovalConfigId { get; set; } + + public int StockAdjustmentLevels { get; set; } = 2; + /// With 2 levels: an adjustment whose stock value is below this needs only one approval. 0 = always two. + public decimal StockAdjustmentLevel2ThresholdValue { get; set; } + + public int PriceAdjustmentLevels { get; set; } = 2; + /// With 2 levels: a change smaller than this percent (largest across its lines) needs only one approval. 0 = always two. + public decimal PriceAdjustmentLevel2ThresholdPercent { get; set; } + + /// Comma-separated role codes allowed to give the second approval; empty = anyone with the Approve action. + public string? Level2RoleCodes { get; set; } + + public DateTime? UpdatedAt { get; set; } + public int? UpdatedBy { get; set; } + public uint RowVersion { get; set; } +} diff --git a/Backend/ERPCore/Domain/Entities/Expense.cs b/Backend/ERPCore/Domain/Entities/Expense.cs new file mode 100644 index 0000000..14c4f3b --- /dev/null +++ b/Backend/ERPCore/Domain/Entities/Expense.cs @@ -0,0 +1,92 @@ +using ERPCore.Domain.Enums; + +namespace ERPCore.Domain.Entities; + +/// +/// One business expense bill (electricity, rent, …) outside procurement. Draft is editable; +/// Post accrues it to GL (Debit the title's expense account / Credit Accrued Expenses); +/// payments then settle the accrual in one or more installments (see +/// ExpensePaymentService). Model: docs/EXPENSE_MODULE_PLAN.md §3.3. +/// +public class Expense +{ + public int ExpenseId { get; set; } + public string DocNo { get; set; } = string.Empty; + + public int BranchId { get; set; } + public Branch? Branch { get; set; } + + public DateOnly ExpenseDate { get; set; } + public DateOnly? DueDate { get; set; } + + public int ExpenseCategoryId { get; set; } + public ExpenseCategory? Category { get; set; } + + public int ExpenseTitleId { get; set; } + public ExpenseTitle? Title { get; set; } + + public string? Payee { get; set; } + public int? VendorId { get; set; } + public Vendor? Vendor { get; set; } + + public string? BillReference { get; set; } + public string? Description { get; set; } + /// Optional month the bill covers, as yyyy-MM (e.g. September's electricity). + public string? PeriodMonth { get; set; } + + public decimal Amount { get; set; } + public decimal PaidAmount { get; set; } + public decimal BalanceAmount { get; set; } + + public ExpenseStatus Status { get; set; } = ExpenseStatus.Draft; + public ExpensePaymentStatus PaymentStatus { get; set; } = ExpensePaymentStatus.Unpaid; + + /// Snapshot of the title's GL expense account at post time. + public string? GlExpenseAccountCode { get; set; } + /// Journal number of the accrual entry posted on Post. + public string? GlJournalNo { get; set; } + public DateTime? GlPostedAt { get; set; } + /// Journal number of the reversing entry posted on Cancel (Posted expenses only). + public string? GlReversalJournalNo { get; set; } + public string? CancelReason { get; set; } + + public int CreatedBy { get; set; } + public User? Creator { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + + public uint RowVersion { get; set; } + + public ICollection Payments { get; set; } = new List(); +} + +/// +/// A payment against a Posted expense's balance (installments allowed). Every line of one +/// payment submission shares the single GL journal entry that submission posted (Debit +/// Accrued Expenses / Credit each selected cash-or-bank account). Mirrors +/// . +/// +public class ExpensePayment +{ + public int ExpensePaymentId { get; set; } + + public int ExpenseId { get; set; } + public Expense? Expense { get; set; } + + public decimal Amount { get; set; } + public DateTime PaymentDate { get; set; } + public ExpensePaymentMethod PaymentMethod { get; set; } + + /// GL's numeric id for the bank/cash account the payment was made from. + public long GlBankAccountId { get; set; } + /// Snapshot of the account's display name at payment time (GL account lists have no local FK). + public string BankAccountName { get; set; } = string.Empty; + + public string? ChequeNo { get; set; } + public string? Reference { get; set; } + public string? GlJournalNo { get; set; } + + public int CreatedBy { get; set; } + public User? Creator { get; set; } + public DateTime CreatedAt { get; set; } +} diff --git a/Backend/ERPCore/Domain/Entities/ExpenseCategory.cs b/Backend/ERPCore/Domain/Entities/ExpenseCategory.cs new file mode 100644 index 0000000..2062573 --- /dev/null +++ b/Backend/ERPCore/Domain/Entities/ExpenseCategory.cs @@ -0,0 +1,48 @@ +using ERPCore.Domain.Enums; + +namespace ERPCore.Domain.Entities; + +/// +/// Top-level expense grouping (Utility, Rental, Salaries, Other). The four seeded rows are +/// — they can be deactivated but not renamed; admins can add more. +/// Each category owns its s. Model: docs/EXPENSE_MODULE_PLAN.md §3.1. +/// +public class ExpenseCategory +{ + public int ExpenseCategoryId { get; set; } + public string Code { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public bool IsSystem { get; set; } + public EntityStatus Status { get; set; } = EntityStatus.Active; + public int SortOrder { get; set; } + + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public uint RowVersion { get; set; } + + public ICollection Titles { get; set; } = new List(); +} + +/// +/// A specific expense under a category (Utility → Electricity, Water, …). Carries the GL +/// expense account debited when an expense with this title is posted — snapshotted onto the +/// expense at post time, so changing it later does not touch already-posted expenses. +/// Model: docs/EXPENSE_MODULE_PLAN.md §3.2. +/// +public class ExpenseTitle +{ + public int ExpenseTitleId { get; set; } + + public int ExpenseCategoryId { get; set; } + public ExpenseCategory? Category { get; set; } + + public string Name { get; set; } = string.Empty; + public string GlExpenseAccountCode { get; set; } = string.Empty; + public bool IsSystem { get; set; } + public EntityStatus Status { get; set; } = EntityStatus.Active; + public int SortOrder { get; set; } + + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public uint RowVersion { get; set; } +} diff --git a/Backend/ERPCore/Domain/Entities/PriceAdjustment.cs b/Backend/ERPCore/Domain/Entities/PriceAdjustment.cs new file mode 100644 index 0000000..0089dd4 --- /dev/null +++ b/Backend/ERPCore/Domain/Entities/PriceAdjustment.cs @@ -0,0 +1,78 @@ +using ERPCore.Domain.Enums; + +namespace ERPCore.Domain.Entities; + +/// +/// A request to change items' selling prices (docs/DUAL_APPROVAL_PLAN.md §6). Nothing changes on the +/// items until it is fully approved; then each line's new prices are written to its item and an +/// row is kept. is the requester. +/// +public class PriceAdjustment +{ + public int PriceAdjustmentId { get; set; } + public string DocNo { get; set; } = string.Empty; + public string Reason { get; set; } = string.Empty; + + public PriceAdjustmentStatus Status { get; set; } = PriceAdjustmentStatus.PendingApproval; + public int RequiredApprovals { get; set; } + + public int CreatedBy { get; set; } + public User? Creator { get; set; } + public DateTime CreatedAt { get; set; } + + public int? ApprovedLevel1By { get; set; } + public DateTime? ApprovedLevel1At { get; set; } + public int? ApprovedLevel2By { get; set; } + public DateTime? ApprovedLevel2At { get; set; } + public int? RejectedBy { get; set; } + public DateTime? RejectedAt { get; set; } + public string? RejectReason { get; set; } + public DateTime? CancelledAt { get; set; } + /// When the new prices were written to the items. + public DateTime? AppliedAt { get; set; } + + public uint RowVersion { get; set; } + + public ICollection Lines { get; set; } = new List(); +} + +/// One item's change. The Old* values are the item's prices when the request was made — +/// approval fails if the item has been repriced since (stale request). +public class PriceAdjustmentLine +{ + public int PriceAdjustmentLineId { get; set; } + public int PriceAdjustmentId { get; set; } + public PriceAdjustment? PriceAdjustment { get; set; } + + public int ItemId { get; set; } + public Item? Item { get; set; } + + public decimal? OldMinPrice { get; set; } + public decimal? OldMaxPrice { get; set; } + public decimal? OldFlatPrice { get; set; } + public decimal? NewMinPrice { get; set; } + public decimal? NewMaxPrice { get; set; } + public decimal? NewFlatPrice { get; set; } +} + +/// An applied selling-price change: what it was, what it became, and who approved it. +public class ItemPriceHistory +{ + public int ItemPriceHistoryId { get; set; } + public int ItemId { get; set; } + public Item? Item { get; set; } + public int? PriceAdjustmentId { get; set; } + public PriceAdjustment? PriceAdjustment { get; set; } + + public decimal? OldMinPrice { get; set; } + public decimal? OldMaxPrice { get; set; } + public decimal? OldFlatPrice { get; set; } + public decimal? NewMinPrice { get; set; } + public decimal? NewMaxPrice { get; set; } + public decimal? NewFlatPrice { get; set; } + + public int RequestedBy { get; set; } + public int? ApprovedLevel1By { get; set; } + public int? ApprovedLevel2By { get; set; } + public DateTime AppliedAt { get; set; } +} diff --git a/Backend/ERPCore/Domain/Entities/SalesInvoicePayment.cs b/Backend/ERPCore/Domain/Entities/SalesInvoicePayment.cs index 27705b3..20c2e97 100644 --- a/Backend/ERPCore/Domain/Entities/SalesInvoicePayment.cs +++ b/Backend/ERPCore/Domain/Entities/SalesInvoicePayment.cs @@ -1,3 +1,5 @@ +using ERPCore.Domain.Enums; + namespace ERPCore.Domain.Entities; /// @@ -21,6 +23,9 @@ public class SalesInvoicePayment /// Snapshot of the account's display name at payment time (GL account lists have no local FK). public string BankAccountName { get; set; } = string.Empty; + /// How the customer paid. Payments recorded before this existed were backfilled as Cash. + public SalesPaymentMethod PaymentMethod { get; set; } = SalesPaymentMethod.Cash; + public string? Reference { get; set; } /// Journal number of the real GL journal entry this payment posted. diff --git a/Backend/ERPCore/Domain/Entities/SalesSlip.cs b/Backend/ERPCore/Domain/Entities/SalesSlip.cs index c3d713f..eabcda7 100644 --- a/Backend/ERPCore/Domain/Entities/SalesSlip.cs +++ b/Backend/ERPCore/Domain/Entities/SalesSlip.cs @@ -27,6 +27,11 @@ public class SalesSlip public decimal PaidAmount { get; set; } public decimal BalanceAmount { get; set; } + /// How was tendered: CashAmount + CardAmount == PaidAmount. + /// Slips from before the split existed were backfilled as all cash. + public decimal CashAmount { get; set; } + public decimal CardAmount { get; set; } + /// Set once this slip's business date is closed by — a closed slip is frozen (no further edits). public int? DayEndId { get; set; } public SalesDayEnd? DayEnd { get; set; } diff --git a/Backend/ERPCore/Domain/Entities/StockAdjustment.cs b/Backend/ERPCore/Domain/Entities/StockAdjustment.cs index bcece95..8f72fd1 100644 --- a/Backend/ERPCore/Domain/Entities/StockAdjustment.cs +++ b/Backend/ERPCore/Domain/Entities/StockAdjustment.cs @@ -37,5 +37,20 @@ public class StockAdjustment public string? GlJournalNo { get; set; } public DateTime? GlPostedAt { get; set; } + // --- Approval (docs/DUAL_APPROVAL_PLAN.md §5). CreatedBy is the requester. --- + + /// Approvals this request needs (0 = posted on create, 1 or 2), fixed when it is made. + public int RequiredApprovals { get; set; } + public int? ApprovedLevel1By { get; set; } + public DateTime? ApprovedLevel1At { get; set; } + public int? ApprovedLevel2By { get; set; } + public DateTime? ApprovedLevel2At { get; set; } + public int? RejectedBy { get; set; } + public DateTime? RejectedAt { get; set; } + public string? RejectReason { get; set; } + public DateTime? CancelledAt { get; set; } + /// When stock actually moved — the ledger and GL use this, not . + public DateTime? PostedAt { get; set; } + public ICollection Lines { get; set; } = new List(); } diff --git a/Backend/ERPCore/Domain/Entities/StockAdjustmentLine.cs b/Backend/ERPCore/Domain/Entities/StockAdjustmentLine.cs index bcbcc84..ecab917 100644 --- a/Backend/ERPCore/Domain/Entities/StockAdjustmentLine.cs +++ b/Backend/ERPCore/Domain/Entities/StockAdjustmentLine.cs @@ -21,4 +21,12 @@ public class StockAdjustmentLine public Serial? Serial { get; set; } public decimal QtyDelta { get; set; } + + /// + /// The serials named on the request, kept until it posts (JSON arrays): outbound serial ids + /// to write off, inbound serial numbers found. Re-validated at posting, because a pending + /// request moves nothing and the serials may have moved in the meantime. + /// + public string? RequestedSerialIds { get; set; } + public string? RequestedSerialNos { get; set; } } diff --git a/Backend/ERPCore/Domain/Enums/AdjustmentStatus.cs b/Backend/ERPCore/Domain/Enums/AdjustmentStatus.cs index 7d56708..1301e79 100644 --- a/Backend/ERPCore/Domain/Enums/AdjustmentStatus.cs +++ b/Backend/ERPCore/Domain/Enums/AdjustmentStatus.cs @@ -1,13 +1,33 @@ namespace ERPCore.Domain.Enums; /// -/// Stock-adjustment lifecycle (docs/10 §B.8.1). Phase 1 auto-posts, so -/// is reserved for the future threshold-approval -/// workflow (FR-STK-07). Stored as a string. +/// Stock-adjustment lifecycle (docs/DUAL_APPROVAL_PLAN.md §5.1). A new adjustment is a request +/// (, then when two approvals are +/// required) and only moves stock when it becomes . With approvals switched off +/// (ApprovalConfig.StockAdjustmentLevels = 0) it posts on create, as before. Stored as a string. /// public enum AdjustmentStatus { Draft, PendingApproval, - Posted + Posted, + PendingSecondApproval, + Rejected, + Cancelled +} + +/// Price-adjustment lifecycle — same request flow as ; means applied. +public enum PriceAdjustmentStatus +{ + PendingApproval, + PendingSecondApproval, + Approved, + Rejected, + Cancelled +} + +public enum ApprovalDecision +{ + Approved, + Rejected } diff --git a/Backend/ERPCore/Domain/Enums/BankDepositStatus.cs b/Backend/ERPCore/Domain/Enums/BankDepositStatus.cs new file mode 100644 index 0000000..1e530f0 --- /dev/null +++ b/Backend/ERPCore/Domain/Enums/BankDepositStatus.cs @@ -0,0 +1,8 @@ +namespace ERPCore.Domain.Enums; + +/// A deposit is posted as soon as it is saved; a wrong one is cancelled (GL reversed) and re-entered. +public enum BankDepositStatus +{ + Posted, + Cancelled +} diff --git a/Backend/ERPCore/Domain/Enums/ExpenseStatus.cs b/Backend/ERPCore/Domain/Enums/ExpenseStatus.cs new file mode 100644 index 0000000..6dc3236 --- /dev/null +++ b/Backend/ERPCore/Domain/Enums/ExpenseStatus.cs @@ -0,0 +1,24 @@ +namespace ERPCore.Domain.Enums; + +public enum ExpenseStatus +{ + Draft, + Posted, + Cancelled +} + +/// Settlement state of a Posted expense, derived from its paid/balance amounts. +public enum ExpensePaymentStatus +{ + Unpaid, + PartiallyPaid, + Paid +} + +public enum ExpensePaymentMethod +{ + Cash, + BankTransfer, + Cheque, + Card +} diff --git a/Backend/ERPCore/Domain/Enums/SalesPaymentMethod.cs b/Backend/ERPCore/Domain/Enums/SalesPaymentMethod.cs new file mode 100644 index 0000000..43f461b --- /dev/null +++ b/Backend/ERPCore/Domain/Enums/SalesPaymentMethod.cs @@ -0,0 +1,12 @@ +namespace ERPCore.Domain.Enums; + +/// How a customer paid for a sale — recorded per invoice payment (and split into +/// cash/card amounts on slips and bundle sales) so the Day-End Sales Report can break +/// collections down by method. Mirrors . +public enum SalesPaymentMethod +{ + Cash, + Card, + BankTransfer, + Cheque +} diff --git a/Backend/ERPCore/Domain/Notifications/Modules/NotificationModules.cs b/Backend/ERPCore/Domain/Notifications/Modules/NotificationModules.cs index 49c4efb..831df17 100644 --- a/Backend/ERPCore/Domain/Notifications/Modules/NotificationModules.cs +++ b/Backend/ERPCore/Domain/Notifications/Modules/NotificationModules.cs @@ -27,6 +27,18 @@ public sealed class StockAdjustmentCreatedNotificationModule; [NotificationModule("stock.transfer.created", "Stock Transfer Created", "Raised when a stock transfer is created.", 90)] public sealed class StockTransferCreatedNotificationModule; +[NotificationModule("stock.low", "Low Stock / Reorder Alert", "Raised when a sale, transfer or adjustment takes an item's stock at a warehouse to or below its reorder point.", 95)] +public sealed class StockLowNotificationModule; + +[NotificationModule("approval.pending", "Approval Needed", "Sent to the people who can approve a stock or price adjustment that is waiting for them.", 96)] +public sealed class ApprovalPendingNotificationModule; + +[NotificationModule("approval.approved", "Request Approved", "Sent to the requester when their stock or price adjustment is fully approved and applied.", 97)] +public sealed class ApprovalApprovedNotificationModule; + +[NotificationModule("approval.rejected", "Request Rejected", "Sent to the requester when their stock or price adjustment is rejected.", 98)] +public sealed class ApprovalRejectedNotificationModule; + [NotificationModule("item.created", "Item Created", "Raised when an item master record is created.", 100)] public sealed class ItemCreatedNotificationModule; diff --git a/Backend/ERPCore/Dtos/Accounts/BankDepositDtos.cs b/Backend/ERPCore/Dtos/Accounts/BankDepositDtos.cs new file mode 100644 index 0000000..25d86b0 --- /dev/null +++ b/Backend/ERPCore/Dtos/Accounts/BankDepositDtos.cs @@ -0,0 +1,66 @@ +using System.ComponentModel.DataAnnotations; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Common; + +namespace ERPCore.Dtos.Accounts; + +// --- Cash in hand (docs/BANK_DEPOSIT_MODULE_PLAN.md §2, §7.1) --- + +/// +/// One business day's till cash. CashIn is slip/bundle cash plus cash invoice payments received +/// that day; CashOut is expense payments made in cash; Deposited is Posted bank deposits for that +/// business day. Closing = Opening + NetCash − Deposited, and becomes the next day's Opening. +/// Days before the tracking start date carry nothing in: their Opening is 0. +/// +public sealed record CashPositionDto( + DateOnly Date, decimal Opening, decimal CashIn, decimal CashOut, decimal NetCash, decimal Deposited, decimal Closing); + +/// +/// A day's cash position for the deposit form. is the lowest +/// closing balance from that day up to today: a back-dated deposit lowers every later day too. +/// is the GL cash account the form preselects. +/// +public sealed record CashInHandDto( + CashPositionDto Position, decimal AvailableToDeposit, int? BranchId, DateOnly? TrackingStartDate, string TimeZone, + string? DefaultCashAccountCode); + +public sealed record CashInHandDailyDto( + DateOnly From, DateOnly To, IReadOnlyList Days, int? BranchId, DateOnly? TrackingStartDate, string TimeZone); + +// --- Deposits --- + +public sealed record BankDepositDto( + int BankDepositId, string DocNo, int BranchId, string? BranchName, + DateOnly BusinessDate, DateOnly DepositDate, decimal Amount, + long FromGlCashAccountId, string FromCashAccountCode, string FromCashAccountName, + long ToGlBankAccountId, string ToBankAccountCode, string ToBankAccountName, string? ToBankName, string? ToAccountNumber, + string? DepositSlipNo, string? DepositedBy, string? Remarks, + BankDepositStatus Status, string? GlJournalNo, DateTime? GlPostedAt, + string? GlReversalJournalNo, string? CancelReason, DateTime? CancelledAt, + int CreatedBy, DateTime CreatedAt); + +/// A page of deposits plus the total amount of every row matching the filter (not just this page). +public sealed record BankDepositListDto(IReadOnlyList Items, PaginationDto Pagination, decimal TotalAmount); + +public sealed record BankDepositListFilter( + DateOnly? From, DateOnly? To, int? BranchId, long? BankAccountId, BankDepositStatus? Status); + +public sealed class CreateBankDepositRequest +{ + /// Required when branching is enabled; otherwise the default branch is used. A branch user always deposits for their own branch. + public int BranchId { get; set; } + [Required] public DateOnly BusinessDate { get; set; } + /// When the bank received it; also the GL entry date. Omitted ⇒ the business date. + public DateOnly? DepositDate { get; set; } + [Range(0.01, 999_999_999_999)] public decimal Amount { get; set; } + [Required] public long FromGlCashAccountId { get; set; } + [Required] public long ToGlBankAccountId { get; set; } + [StringLength(50)] public string? DepositSlipNo { get; set; } + [StringLength(150)] public string? DepositedBy { get; set; } + [StringLength(500)] public string? Remarks { get; set; } +} + +public sealed class CancelBankDepositRequest +{ + [Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty; +} diff --git a/Backend/ERPCore/Dtos/Approvals/ApprovalDtos.cs b/Backend/ERPCore/Dtos/Approvals/ApprovalDtos.cs new file mode 100644 index 0000000..94eaf00 --- /dev/null +++ b/Backend/ERPCore/Dtos/Approvals/ApprovalDtos.cs @@ -0,0 +1,43 @@ +using System.ComponentModel.DataAnnotations; +using ERPCore.Domain.Enums; + +namespace ERPCore.Dtos.Approvals; + +/// One decision in a document's approval trail. +public sealed record ApprovalStepDto(int Level, ApprovalDecision Decision, int DecidedBy, string? DecidedByName, DateTime DecidedAt, string? Comment); + +/// Settings → Approvals (docs/DUAL_APPROVAL_PLAN.md §7). +public sealed record ApprovalConfigDto( + int StockAdjustmentLevels, decimal StockAdjustmentLevel2ThresholdValue, + int PriceAdjustmentLevels, decimal PriceAdjustmentLevel2ThresholdPercent, + IReadOnlyList Level2RoleCodes, DateTime? UpdatedAt); + +public sealed class UpdateApprovalConfigRequest +{ + [Range(0, 2)] public int StockAdjustmentLevels { get; set; } + [Range(0, double.MaxValue)] public decimal StockAdjustmentLevel2ThresholdValue { get; set; } + [Range(0, 2)] public int PriceAdjustmentLevels { get; set; } + [Range(0, 1000)] public decimal PriceAdjustmentLevel2ThresholdPercent { get; set; } + public List Level2RoleCodes { get; set; } = new(); +} + +public sealed class ApproveRequest +{ + [StringLength(250)] public string? Comment { get; set; } +} + +public sealed class RejectRequest +{ + [Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty; +} + +/// +/// A request waiting in the Approvals inbox. is the approval it waits for +/// (1 or 2) of ; says whether the +/// current user may decide it (not the requester, not the level-1 approver, has the right role). +/// +public sealed record PendingApprovalDto( + string DocType, int DocId, string DocNo, string Summary, int Level, int RequiredApprovals, + int RequestedBy, string? RequestedByName, DateTime RequestedAt, bool CanApprove, string? WhyNot); + +public sealed record PendingApprovalsDto(IReadOnlyList Items, int ActionableCount); diff --git a/Backend/ERPCore/Dtos/Customers/CustomerDtos.cs b/Backend/ERPCore/Dtos/Customers/CustomerDtos.cs index 8176c60..473093b 100644 --- a/Backend/ERPCore/Dtos/Customers/CustomerDtos.cs +++ b/Backend/ERPCore/Dtos/Customers/CustomerDtos.cs @@ -4,6 +4,9 @@ using ERPCore.Domain.Enums; namespace ERPCore.Dtos.Customers; /// Customer resource used by sales documents. +/// Unpaid balance across the customer's Posted sales invoices. Filled on the list only. +/// Posted sales invoices with a balance still due. Filled on the list only. +/// When the customer last paid against an invoice; null if never. Filled on the list only. public sealed record CustomerDto( int CustomerId, string CustomerCode, @@ -23,7 +26,10 @@ public sealed record CustomerDto( EntityStatus Status, int BranchId, DateTime CreatedAt, - DateTime? UpdatedAt); + DateTime? UpdatedAt, + decimal OutstandingAmount = 0m, + int OutstandingInvoiceCount = 0, + DateTime? LastPaymentDate = null); public sealed class CreateCustomerRequest { diff --git a/Backend/ERPCore/Dtos/Expenses/ExpenseDtos.cs b/Backend/ERPCore/Dtos/Expenses/ExpenseDtos.cs new file mode 100644 index 0000000..8d756f4 --- /dev/null +++ b/Backend/ERPCore/Dtos/Expenses/ExpenseDtos.cs @@ -0,0 +1,120 @@ +using System.ComponentModel.DataAnnotations; +using ERPCore.Domain.Enums; + +namespace ERPCore.Dtos.Expenses; + +// --- Setup: categories and titles (docs/EXPENSE_MODULE_PLAN.md §6 "Setup") --- + +public sealed record ExpenseTitleDto( + int ExpenseTitleId, int ExpenseCategoryId, string Name, string GlExpenseAccountCode, + bool IsSystem, EntityStatus Status, int SortOrder); + +public sealed record ExpenseCategoryDto( + int ExpenseCategoryId, string Code, string Name, bool IsSystem, EntityStatus Status, int SortOrder, + IReadOnlyList Titles); + +public sealed class UpdateExpenseCategoryRequest +{ + /// Ignored-if-unchanged for system rows; a different name on a system row is rejected. + [Required, StringLength(100, MinimumLength = 1)] public string Name { get; set; } = string.Empty; + public EntityStatus Status { get; set; } = EntityStatus.Active; +} + +public sealed class CreateExpenseTitleRequest +{ + [Required, StringLength(100, MinimumLength = 1)] public string Name { get; set; } = string.Empty; + [Required, StringLength(20, MinimumLength = 1)] public string GlExpenseAccountCode { get; set; } = string.Empty; +} + +public sealed class UpdateExpenseTitleRequest +{ + /// Ignored-if-unchanged for system rows; a different name on a system row is rejected. + [Required, StringLength(100, MinimumLength = 1)] public string Name { get; set; } = string.Empty; + [Required, StringLength(20, MinimumLength = 1)] public string GlExpenseAccountCode { get; set; } = string.Empty; + public EntityStatus Status { get; set; } = EntityStatus.Active; +} + +// --- Expenses --- + +public sealed record ExpenseSummaryDto( + int ExpenseId, string DocNo, int BranchId, DateOnly ExpenseDate, DateOnly? DueDate, + int ExpenseCategoryId, string CategoryName, int ExpenseTitleId, string TitleName, + string? Payee, string? BillReference, string? PeriodMonth, + decimal Amount, decimal PaidAmount, decimal BalanceAmount, + ExpenseStatus Status, ExpensePaymentStatus PaymentStatus, string? GlJournalNo, DateTime CreatedAt); + +public sealed record ExpensePaymentDto( + int ExpensePaymentId, int ExpenseId, decimal Amount, DateTime PaymentDate, ExpensePaymentMethod PaymentMethod, + long GlBankAccountId, string BankAccountName, string? ChequeNo, string? Reference, string? GlJournalNo, DateTime CreatedAt); + +public sealed record ExpenseDto( + int ExpenseId, string DocNo, int BranchId, DateOnly ExpenseDate, DateOnly? DueDate, + int ExpenseCategoryId, string CategoryName, int ExpenseTitleId, string TitleName, + string? Payee, int? VendorId, string? VendorName, string? BillReference, string? Description, string? PeriodMonth, + decimal Amount, decimal PaidAmount, decimal BalanceAmount, + ExpenseStatus Status, ExpensePaymentStatus PaymentStatus, + string? GlExpenseAccountCode, string? GlJournalNo, DateTime? GlPostedAt, string? GlReversalJournalNo, string? CancelReason, + int CreatedBy, DateTime CreatedAt, DateTime? UpdatedAt, + IReadOnlyList Payments); + +public class ExpenseRequestBase +{ + /// Required when branching is enabled; otherwise the default branch is used. + public int BranchId { get; set; } + [Required] public DateOnly ExpenseDate { get; set; } + public DateOnly? DueDate { get; set; } + [Required] public int ExpenseCategoryId { get; set; } + [Required] public int ExpenseTitleId { get; set; } + [StringLength(150)] public string? Payee { get; set; } + public int? VendorId { get; set; } + [StringLength(50)] public string? BillReference { get; set; } + [StringLength(500)] public string? Description { get; set; } + [RegularExpression(@"^\d{4}-(0[1-9]|1[0-2])$", ErrorMessage = "PeriodMonth must be yyyy-MM.")] + public string? PeriodMonth { get; set; } + [Range(0.01, 999_999_999_999)] public decimal Amount { get; set; } +} + +public sealed class CreateExpenseRequest : ExpenseRequestBase; + +public sealed class UpdateExpenseRequest : ExpenseRequestBase; + +public sealed class CancelExpenseRequest +{ + [Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty; +} + +public sealed class ExpensePaymentLineRequest +{ + [Required] public long GlBankAccountId { get; set; } + [Range(0.01, double.MaxValue)] public decimal Amount { get; set; } +} + +/// +/// One payment submission: one or more lines (e.g. part cash, part bank), all posted in a +/// single GL journal entry. Total must not exceed the balance (for Post & Pay, the amount); partial is fine. +/// +public sealed class CreateExpensePaymentRequest +{ + [Required, MinLength(1)] public List Lines { get; set; } = new(); + public ExpensePaymentMethod PaymentMethod { get; set; } = ExpensePaymentMethod.Cash; + /// Required when is Cheque. + [StringLength(30)] public string? ChequeNo { get; set; } + [StringLength(100)] public string? Reference { get; set; } + /// When it was paid; also the GL entry date. Omitted ⇒ today. Never in the future. + public DateOnly? PaymentDate { get; set; } +} + +public sealed record ExpenseListFilter( + ExpenseStatus? Status, ExpensePaymentStatus? PaymentStatus, int? CategoryId, int? TitleId, + DateOnly? From, DateOnly? To, bool OverdueOnly); + +public sealed record ExpenseTitleTotalDto(int ExpenseTitleId, string TitleName, decimal Amount, int Count); + +public sealed record ExpenseCategoryTotalDto( + int ExpenseCategoryId, string CategoryName, decimal Amount, int Count, IReadOnlyList Titles); + +/// Posted (non-cancelled) expenses dated within [From, To], plus the all-time unpaid/overdue position. +public sealed record ExpenseSummaryReportDto( + DateOnly From, DateOnly To, decimal TotalAmount, decimal PaidAmount, decimal UnpaidAmount, int Count, + decimal OutstandingAmount, decimal OverdueAmount, int OverdueCount, int DraftCount, + IReadOnlyList Categories); diff --git a/Backend/ERPCore/Dtos/Items/ItemOverviewDtos.cs b/Backend/ERPCore/Dtos/Items/ItemOverviewDtos.cs index 438ef01..164a11b 100644 --- a/Backend/ERPCore/Dtos/Items/ItemOverviewDtos.cs +++ b/Backend/ERPCore/Dtos/Items/ItemOverviewDtos.cs @@ -2,6 +2,14 @@ using ERPCore.Domain.Enums; namespace ERPCore.Dtos.Items; +/// +/// One posted GRN receipt of an item — what it was bought at. is the +/// cost after trade discount (the FIFO stock cost, VAT excluded) — the figure to price against. +/// +public sealed record ItemPurchasePriceDto( + int GrnId, string GrnDocNo, DateTime ReceivedAt, int VendorId, string? VendorName, string? WarehouseCode, + decimal Qty, decimal UnitCost, decimal DiscountPct, decimal NetUnitCost, decimal VatPct); + /// /// Item row enriched with cross-module aggregates (current FIFO stock value, low-stock /// flag, last purchase, trailing-30-day sales) for the Items list page. Deliberately a diff --git a/Backend/ERPCore/Dtos/Products/PriceAdjustmentDtos.cs b/Backend/ERPCore/Dtos/Products/PriceAdjustmentDtos.cs new file mode 100644 index 0000000..99c1b28 --- /dev/null +++ b/Backend/ERPCore/Dtos/Products/PriceAdjustmentDtos.cs @@ -0,0 +1,47 @@ +using System.ComponentModel.DataAnnotations; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Approvals; + +namespace ERPCore.Dtos.Products; + +/// One item's requested change. Current* are the item's prices now — they differ from Old* when it was repriced since. +public sealed record PriceAdjustmentLineDto( + int PriceAdjustmentLineId, int ItemId, string Sku, string ItemName, + decimal? OldMinPrice, decimal? OldMaxPrice, decimal? OldFlatPrice, + decimal? NewMinPrice, decimal? NewMaxPrice, decimal? NewFlatPrice, + decimal? CurrentMinPrice, decimal? CurrentMaxPrice, decimal? CurrentFlatPrice); + +public sealed record PriceAdjustmentSummaryDto( + int PriceAdjustmentId, string DocNo, string Reason, PriceAdjustmentStatus Status, int RequiredApprovals, int? PendingLevel, + int LineCount, int CreatedBy, string? CreatedByName, DateTime CreatedAt, DateTime? AppliedAt); + +/// The approval it waits for (1 or 2); null once decided. +public sealed record PriceAdjustmentDto( + int PriceAdjustmentId, string DocNo, string Reason, PriceAdjustmentStatus Status, int RequiredApprovals, int? PendingLevel, + int CreatedBy, string? CreatedByName, DateTime CreatedAt, + int? ApprovedLevel1By, DateTime? ApprovedLevel1At, int? ApprovedLevel2By, DateTime? ApprovedLevel2At, + int? RejectedBy, DateTime? RejectedAt, string? RejectReason, DateTime? CancelledAt, DateTime? AppliedAt, + IReadOnlyList Lines, IReadOnlyList ApprovalHistory, bool CanApprove, string? WhyNot, + bool CanWithdraw); + +public sealed class PriceAdjustmentLineInput +{ + [Required] public int ItemId { get; set; } + /// The prices the item should have afterwards — null clears that price. + [Range(0, double.MaxValue)] public decimal? NewMinPrice { get; set; } + [Range(0, double.MaxValue)] public decimal? NewMaxPrice { get; set; } + [Range(0, double.MaxValue)] public decimal? NewFlatPrice { get; set; } +} + +public sealed class CreatePriceAdjustmentRequest +{ + [Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty; + [Required, MinLength(1)] public List Lines { get; set; } = new(); +} + +public sealed record ItemPriceHistoryDto( + int ItemPriceHistoryId, int ItemId, int? PriceAdjustmentId, string? DocNo, + decimal? OldMinPrice, decimal? OldMaxPrice, decimal? OldFlatPrice, + decimal? NewMinPrice, decimal? NewMaxPrice, decimal? NewFlatPrice, + int RequestedBy, string? RequestedByName, int? ApprovedLevel1By, string? ApprovedLevel1ByName, + int? ApprovedLevel2By, string? ApprovedLevel2ByName, DateTime AppliedAt); diff --git a/Backend/ERPCore/Dtos/Reports/DayEndReportDtos.cs b/Backend/ERPCore/Dtos/Reports/DayEndReportDtos.cs new file mode 100644 index 0000000..5a13c14 --- /dev/null +++ b/Backend/ERPCore/Dtos/Reports/DayEndReportDtos.cs @@ -0,0 +1,51 @@ +namespace ERPCore.Dtos.Reports; + +/// +/// One business day's figures (or, with null, a range's totals) — docs/DAY_END_SALES_REPORT_PLAN.md §4. +/// Sales are Posted slips, bundle sales and invoices dated that day; Paid is what has been paid on +/// those sales so far (split by how it was paid) and Due the rest. +/// +/// TotalSales − ExpenseTotal (sales returns aren't priced, so they can't be netted off). +/// FIFO cost of the stock those sales issued (the stock ledger's Out value). +/// TotalSales − CostOfGoodsSold. +/// GrossProfit − ExpenseTotal; negative is a loss. +/// Invoice payments received this day against invoices from +/// earlier days — not part of PaidAmount. +/// Cash that came in this day (slip/bundle cash + cash invoice payments, any +/// invoice) minus expenses paid in cash this day — a till check. +/// Cash left in the till from earlier days (docs/BANK_DEPOSIT_MODULE_PLAN.md §9). +/// For a range's totals, the first day's opening. +/// Posted bank deposits made against this business day. +/// Opening + NetCash − CashDeposited: cash still in hand. For a range's totals, the last day's closing. +public sealed record DayEndReportDayDto( + DateOnly? Date, + decimal TotalSales, decimal PaidAmount, decimal DueAmount, + decimal PaidByCash, decimal PaidByCard, decimal PaidByOther, + int SalesCount, int CashSalesCount, int CardSalesCount, int MixedSalesCount, int OtherSalesCount, int UnpaidSalesCount, + decimal ExpenseTotal, decimal Income, + decimal CostOfGoodsSold, decimal GrossProfit, decimal NetProfit, + decimal EarlierInvoiceCollections, + decimal NetCash, + decimal OpeningCashInHand, decimal CashDeposited, decimal ClosingCashInHand); + +/// One sale of the day. is Cash, Card, Bank transfer, Cheque, Mixed or Unpaid. +public sealed record DayEndReportSaleDto( + string DocType, int DocId, string DocNo, DateTime At, string CustomerName, + decimal Total, decimal Paid, decimal Due, string PaidBy, decimal Cost, decimal GrossProfit); + +public sealed record DayEndReportExpenseDto( + int ExpenseId, string DocNo, string CategoryName, string TitleName, string? Payee, decimal Amount, decimal PaidAmount); + +/// A Posted bank deposit of this business day's cash. +public sealed record DayEndReportDepositDto( + int BankDepositId, string DocNo, string BankAccountName, string? BankName, string? AccountNumber, + string? DepositSlipNo, DateOnly DepositDate, decimal Amount); + +/// A single day: its figures plus the sales, expenses and bank deposits behind them. +public sealed record DayEndReportDto( + DayEndReportDayDto Summary, IReadOnlyList Sales, IReadOnlyList Expenses, + IReadOnlyList Deposits, int? BranchId, string TimeZone); + +/// Day-wise figures for a range (every day present, zeros on quiet days) plus the range's totals. +public sealed record DayEndDailyReportDto( + DateOnly From, DateOnly To, IReadOnlyList Days, DayEndReportDayDto Totals, int? BranchId, string TimeZone); diff --git a/Backend/ERPCore/Dtos/Sales/BundleSaleDtos.cs b/Backend/ERPCore/Dtos/Sales/BundleSaleDtos.cs index 9ab6b6b..80a6e19 100644 --- a/Backend/ERPCore/Dtos/Sales/BundleSaleDtos.cs +++ b/Backend/ERPCore/Dtos/Sales/BundleSaleDtos.cs @@ -12,7 +12,7 @@ public sealed record BundleSaleDto( int WarehouseId, int CashierUserId, int BundleSaleTemplateId, string BundleName, string BundleCode, BundleSaleStatus Status, decimal ComponentSubtotal, decimal BundlePrice, decimal MarginAmount, decimal DiscountTotal, decimal TaxTotal, decimal GrandTotal, DateTime CreatedAt, DateTime? UpdatedAt, - IReadOnlyList Lines); + IReadOnlyList Lines, decimal CashAmount, decimal CardAmount); public sealed record BundleSaleSummaryDto( int BundleSaleId, string BundleNo, DateTime BundleDate, int CustomerId, string CustomerSnapshotName, @@ -66,6 +66,8 @@ public sealed class CreateBundleSaleRequest [Range(0, double.MaxValue)] public decimal BundlePrice { get; set; } public bool AllowPriceOverride { get; set; } [Required, MinLength(1)] public List Lines { get; set; } = new(); + /// The part of the total tendered by card; the rest is cash. Omit for all cash. + [Range(0, double.MaxValue)] public decimal? CardAmount { get; set; } } public sealed class UpdateBundleSaleRequest @@ -78,4 +80,6 @@ public sealed class UpdateBundleSaleRequest [Range(0, double.MaxValue)] public decimal BundlePrice { get; set; } public bool AllowPriceOverride { get; set; } [Required, MinLength(1)] public List Lines { get; set; } = new(); + /// See . + [Range(0, double.MaxValue)] public decimal? CardAmount { get; set; } } diff --git a/Backend/ERPCore/Dtos/Sales/SalesInvoiceDtos.cs b/Backend/ERPCore/Dtos/Sales/SalesInvoiceDtos.cs index 52a51b2..77cd30d 100644 --- a/Backend/ERPCore/Dtos/Sales/SalesInvoiceDtos.cs +++ b/Backend/ERPCore/Dtos/Sales/SalesInvoiceDtos.cs @@ -31,12 +31,15 @@ public sealed record SalesInvoiceSummaryDto( public sealed record SalesInvoicePaymentDto( int SalesInvoicePaymentId, int SalesInvoiceId, decimal Amount, DateTime PaymentDate, - long GlBankAccountId, string BankAccountName, string? Reference, string? GlJournalNo, DateTime CreatedAt); + long GlBankAccountId, string BankAccountName, string? Reference, string? GlJournalNo, DateTime CreatedAt, + SalesPaymentMethod PaymentMethod); public sealed class CreateSalesInvoicePaymentRequest { [Range(0.01, double.MaxValue)] public decimal Amount { get; set; } [Required] public long GlBankAccountId { get; set; } + /// How the customer paid. Omitted ⇒ derived from the account: a Cash account ⇒ Cash, a Bank account ⇒ Card. + public SalesPaymentMethod? PaymentMethod { get; set; } [StringLength(100)] public string? Reference { get; set; } /// When the customer paid; also the GL entry date. Omitted ⇒ today. Never in the future. public DateOnly? PaymentDate { get; set; } @@ -47,6 +50,8 @@ public sealed class BulkSalesInvoicePaymentLine { [Range(0.01, double.MaxValue)] public decimal Amount { get; set; } [Required] public long GlBankAccountId { get; set; } + /// See . + public SalesPaymentMethod? PaymentMethod { get; set; } } /// diff --git a/Backend/ERPCore/Dtos/Sales/SalesSlipDtos.cs b/Backend/ERPCore/Dtos/Sales/SalesSlipDtos.cs index 9c80883..37af479 100644 --- a/Backend/ERPCore/Dtos/Sales/SalesSlipDtos.cs +++ b/Backend/ERPCore/Dtos/Sales/SalesSlipDtos.cs @@ -12,7 +12,7 @@ public sealed record SalesSlipLineDto( public sealed record SalesSlipTotalsDto( decimal Subtotal, decimal DiscountTotal, decimal FreeQtyTotal, decimal TaxTotal, decimal GrandTotal, - decimal PaidAmount, decimal BalanceAmount); + decimal PaidAmount, decimal BalanceAmount, decimal CashAmount, decimal CardAmount); public sealed record SalesSlipDto( int SalesSlipId, string SlipNo, DateTime SlipDate, int CustomerId, @@ -87,6 +87,10 @@ public sealed class CreateSalesSlipRequest /// Receivable when it posts (see SalesDayEndService.CloseAsync). /// [Range(0, double.MaxValue)] public decimal? PaidAmount { get; set; } + + /// The part of the paid amount tendered by card; the rest of it is cash. Omit for all + /// cash. Must be between 0 and the paid amount. + [Range(0, double.MaxValue)] public decimal? CardAmount { get; set; } } public sealed class UpdateSalesSlipRequest @@ -98,4 +102,7 @@ public sealed class UpdateSalesSlipRequest /// See . [Range(0, double.MaxValue)] public decimal? PaidAmount { get; set; } + + /// See . + [Range(0, double.MaxValue)] public decimal? CardAmount { get; set; } } diff --git a/Backend/ERPCore/Dtos/Stock/AdjustmentDtos.cs b/Backend/ERPCore/Dtos/Stock/AdjustmentDtos.cs index 1efd1a1..ab73e59 100644 --- a/Backend/ERPCore/Dtos/Stock/AdjustmentDtos.cs +++ b/Backend/ERPCore/Dtos/Stock/AdjustmentDtos.cs @@ -5,17 +5,29 @@ namespace ERPCore.Dtos.Stock; // Responses (docs/11 §5.5) ------------------------------------------------------ -public sealed record AdjustmentLineDto(int AdjLineId, int ItemId, int? BinId, int? BatchId, decimal QtyDelta, int? SerialId); +/// Serials named to write off (shown while the request is pending). +/// Serial numbers named as found (shown while the request is pending). +public sealed record AdjustmentLineDto( + int AdjLineId, int ItemId, int? BinId, int? BatchId, decimal QtyDelta, int? SerialId, + IReadOnlyList? RequestedSerialIds = null, IReadOnlyList? RequestedSerialNos = null); +/// The approval it waits for (1 or 2); null once decided or posted. +/// Whether the current user may give that approval; says why not. public sealed record AdjustmentDto( int AdjustmentId, string DocNo, int WarehouseId, int BranchId, int ReasonCodeId, AdjustmentStatus Status, int CreatedBy, DateTime CreatedAt, string? GlJournalNo, DateTime? GlPostedAt, - IReadOnlyList Lines, IReadOnlyList LedgerRefs); + IReadOnlyList Lines, IReadOnlyList LedgerRefs, + int RequiredApprovals = 0, int? PendingLevel = null, + int? ApprovedLevel1By = null, DateTime? ApprovedLevel1At = null, int? ApprovedLevel2By = null, DateTime? ApprovedLevel2At = null, + int? RejectedBy = null, DateTime? RejectedAt = null, string? RejectReason = null, DateTime? PostedAt = null, + IReadOnlyList? ApprovalHistory = null, bool CanApprove = false, string? WhyNot = null, + bool CanWithdraw = false, string? CreatedByName = null); /// Row shape for GET /stock-adjustments — no lines/ledgerRefs (those need a per-row query). public sealed record AdjustmentSummaryDto( int AdjustmentId, string DocNo, int WarehouseId, int BranchId, int ReasonCodeId, AdjustmentStatus Status, - int CreatedBy, DateTime CreatedAt, int LineCount, string? GlJournalNo); + int CreatedBy, DateTime CreatedAt, int LineCount, string? GlJournalNo, + int RequiredApprovals = 0, int? PendingLevel = null); // Requests ---------------------------------------------------------------------- diff --git a/Backend/ERPCore/Dtos/Stock/CountDtos.cs b/Backend/ERPCore/Dtos/Stock/CountDtos.cs index ab4a238..95f1618 100644 --- a/Backend/ERPCore/Dtos/Stock/CountDtos.cs +++ b/Backend/ERPCore/Dtos/Stock/CountDtos.cs @@ -18,7 +18,10 @@ public sealed record CountSummaryDto( int CountId, string DocNo, int WarehouseId, int BranchId, CountType CountType, CountStatus Status, int CreatedBy, DateTime CreatedAt, int LineCount); -public sealed record CountPostResultDto(int CountId, CountStatus Status, int? AdjustmentId, string? GlJournalNo, IReadOnlyList LedgerRefs); +/// The variance adjustment's status — PendingApproval when it must be approved before stock moves. +public sealed record CountPostResultDto( + int CountId, CountStatus Status, int? AdjustmentId, string? GlJournalNo, IReadOnlyList LedgerRefs, + AdjustmentStatus? AdjustmentStatus = null, string? AdjustmentDocNo = null); // Requests ---------------------------------------------------------------------- diff --git a/Backend/ERPCore/Infra/Filters/LowStockAlertFilter.cs b/Backend/ERPCore/Infra/Filters/LowStockAlertFilter.cs new file mode 100644 index 0000000..325341a --- /dev/null +++ b/Backend/ERPCore/Infra/Filters/LowStockAlertFilter.cs @@ -0,0 +1,34 @@ +using ERPCore.Services.Stock; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace ERPCore.Infra.Filters; + +/// +/// After every API action that succeeded, checks whether its stock issues pushed anything to or +/// below its reorder point (). By then the action's UoW +/// transaction has committed, so only stock that really left is alerted on. The check is +/// best-effort: a failure is logged and never fails the request that moved the stock. +/// +public sealed class LowStockAlertFilter(LowStockAlertService alerts, LowStockTracker tracker, ILogger logger) : IAsyncActionFilter +{ + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var executed = await next(); + + // A failed action rolled its transaction back: nothing it "issued" actually left stock. + if (executed.Exception is not null && !executed.ExceptionHandled) + { + tracker.Drain(); + return; + } + + try + { + await alerts.CheckAsync(context.HttpContext.RequestAborted); + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + logger.LogWarning(ex, "Low-stock check failed after {Path}", context.HttpContext.Request.Path); + } + } +} diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/ApprovalConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/ApprovalConfiguration.cs new file mode 100644 index 0000000..a301c8d --- /dev/null +++ b/Backend/ERPCore/Infra/Persistence/Configurations/ApprovalConfiguration.cs @@ -0,0 +1,99 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ERPCore.Infra.Persistence.Configurations; + +/// Dual approval tables (docs/DUAL_APPROVAL_PLAN.md §4, §6, §7). +public sealed class DocumentApprovalConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("document_approvals"); + builder.HasKey(a => a.DocumentApprovalId); + + builder.Property(a => a.DocType).IsRequired().HasMaxLength(10); + builder.Property(a => a.Decision).HasConversion().HasMaxLength(20).IsRequired(); + builder.Property(a => a.Comment).HasMaxLength(250); + builder.Property(a => a.DecidedAt).IsRequired(); + + builder.HasOne(a => a.Decider).WithMany().HasForeignKey(a => a.DecidedBy).OnDelete(DeleteBehavior.Restrict); + builder.HasIndex(a => new { a.DocType, a.DocId }); + } +} + +public sealed class ApprovalConfigConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("approval_config", t => + t.HasCheckConstraint("ck_approval_config_singleton", $"`ApprovalConfigId` = {ApprovalConfig.SingletonId}")); + builder.HasKey(c => c.ApprovalConfigId); + builder.Property(c => c.ApprovalConfigId).ValueGeneratedNever(); + + builder.Property(c => c.StockAdjustmentLevel2ThresholdValue).HasPrecision(18, 4); + builder.Property(c => c.PriceAdjustmentLevel2ThresholdPercent).HasPrecision(9, 4); + builder.Property(c => c.Level2RoleCodes).HasMaxLength(500); + + // Dual approval on by default for both documents (plan §8 recommended defaults). + builder.HasData(new ApprovalConfig + { + ApprovalConfigId = ApprovalConfig.SingletonId, + StockAdjustmentLevels = 2, + PriceAdjustmentLevels = 2, + RowVersion = 1 + }); + } +} + +public sealed class PriceAdjustmentConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("price_adjustments"); + builder.HasKey(p => p.PriceAdjustmentId); + + builder.Property(p => p.DocNo).IsRequired().HasMaxLength(30); + builder.HasIndex(p => p.DocNo).IsUnique(); + builder.Property(p => p.Reason).IsRequired().HasMaxLength(250); + builder.Property(p => p.Status).HasConversion().HasMaxLength(30).IsRequired() + .HasDefaultValue(PriceAdjustmentStatus.PendingApproval); + builder.Property(p => p.RejectReason).HasMaxLength(250); + builder.Property(p => p.CreatedAt).IsRequired(); + + builder.HasOne(p => p.Creator).WithMany().HasForeignKey(p => p.CreatedBy).OnDelete(DeleteBehavior.Restrict); + builder.HasMany(p => p.Lines).WithOne(l => l.PriceAdjustment).HasForeignKey(l => l.PriceAdjustmentId).OnDelete(DeleteBehavior.Cascade); + builder.HasIndex(p => p.Status); + } +} + +public sealed class PriceAdjustmentLineConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("price_adjustment_lines"); + builder.HasKey(l => l.PriceAdjustmentLineId); + foreach (var p in new[] { nameof(PriceAdjustmentLine.OldMinPrice), nameof(PriceAdjustmentLine.OldMaxPrice), nameof(PriceAdjustmentLine.OldFlatPrice), + nameof(PriceAdjustmentLine.NewMinPrice), nameof(PriceAdjustmentLine.NewMaxPrice), nameof(PriceAdjustmentLine.NewFlatPrice) }) + builder.Property(p).HasPrecision(18, 4); + builder.HasOne(l => l.Item).WithMany().HasForeignKey(l => l.ItemId).OnDelete(DeleteBehavior.Restrict); + builder.HasIndex(l => l.ItemId); + } +} + +public sealed class ItemPriceHistoryConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("item_price_history"); + builder.HasKey(h => h.ItemPriceHistoryId); + foreach (var p in new[] { nameof(ItemPriceHistory.OldMinPrice), nameof(ItemPriceHistory.OldMaxPrice), nameof(ItemPriceHistory.OldFlatPrice), + nameof(ItemPriceHistory.NewMinPrice), nameof(ItemPriceHistory.NewMaxPrice), nameof(ItemPriceHistory.NewFlatPrice) }) + builder.Property(p).HasPrecision(18, 4); + builder.Property(h => h.AppliedAt).IsRequired(); + builder.HasOne(h => h.Item).WithMany().HasForeignKey(h => h.ItemId).OnDelete(DeleteBehavior.Restrict); + builder.HasOne(h => h.PriceAdjustment).WithMany().HasForeignKey(h => h.PriceAdjustmentId).OnDelete(DeleteBehavior.Restrict); + builder.HasIndex(h => new { h.ItemId, h.AppliedAt }); + } +} diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/BankDepositConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/BankDepositConfiguration.cs new file mode 100644 index 0000000..139622d --- /dev/null +++ b/Backend/ERPCore/Infra/Persistence/Configurations/BankDepositConfiguration.cs @@ -0,0 +1,50 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ERPCore.Infra.Persistence.Configurations; + +/// Bank deposits of till cash (docs/BANK_DEPOSIT_MODULE_PLAN.md §4). +public sealed class BankDepositConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("bank_deposits", t => + t.HasCheckConstraint("ck_bank_deposits_amount", "`Amount` > 0")); + builder.HasKey(x => x.BankDepositId); + + builder.Property(x => x.DocNo).IsRequired().HasMaxLength(30); + builder.HasIndex(x => x.DocNo).IsUnique(); + + builder.Property(x => x.BusinessDate).IsRequired(); + builder.Property(x => x.DepositDate).IsRequired(); + builder.Property(x => x.Amount).HasPrecision(18, 4); + + builder.Property(x => x.FromCashAccountCode).IsRequired().HasMaxLength(20); + builder.Property(x => x.FromCashAccountName).IsRequired().HasMaxLength(200); + builder.Property(x => x.ToBankAccountCode).IsRequired().HasMaxLength(20); + builder.Property(x => x.ToBankAccountName).IsRequired().HasMaxLength(200); + builder.Property(x => x.ToBankName).HasMaxLength(150); + builder.Property(x => x.ToAccountNumber).HasMaxLength(50); + + builder.Property(x => x.DepositSlipNo).HasMaxLength(50); + builder.Property(x => x.DepositedBy).HasMaxLength(150); + builder.Property(x => x.Remarks).HasMaxLength(500); + + builder.Property(x => x.Status) + .HasConversion().HasMaxLength(20).IsRequired() + .HasDefaultValue(BankDepositStatus.Posted); + + builder.Property(x => x.GlJournalNo).HasMaxLength(30); + builder.Property(x => x.GlReversalJournalNo).HasMaxLength(30); + builder.Property(x => x.CancelReason).HasMaxLength(250); + builder.Property(x => x.CreatedAt).IsRequired(); + + builder.HasOne(x => x.Branch).WithMany().HasForeignKey(x => x.BranchId).OnDelete(DeleteBehavior.Restrict); + builder.HasOne(x => x.Creator).WithMany().HasForeignKey(x => x.CreatedBy).OnDelete(DeleteBehavior.Restrict); + + builder.HasIndex(x => new { x.BranchId, x.BusinessDate, x.Status }); + builder.HasIndex(x => x.ToGlBankAccountId); + } +} diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/BundleSaleConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/BundleSaleConfiguration.cs index 3d4b9bd..6a26160 100644 --- a/Backend/ERPCore/Infra/Persistence/Configurations/BundleSaleConfiguration.cs +++ b/Backend/ERPCore/Infra/Persistence/Configurations/BundleSaleConfiguration.cs @@ -26,6 +26,8 @@ public sealed class BundleSaleConfiguration : IEntityTypeConfiguration x.DiscountTotal).HasPrecision(18, 4); builder.Property(x => x.TaxTotal).HasPrecision(18, 4); builder.Property(x => x.GrandTotal).HasPrecision(18, 4); + builder.Property(x => x.CashAmount).HasPrecision(18, 4); + builder.Property(x => x.CardAmount).HasPrecision(18, 4); builder.Property(x => x.CreatedAt).IsRequired(); builder.HasOne(x => x.Customer).WithMany().HasForeignKey(x => x.CustomerId).OnDelete(DeleteBehavior.Restrict); diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/ExpenseConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/ExpenseConfiguration.cs new file mode 100644 index 0000000..1360f78 --- /dev/null +++ b/Backend/ERPCore/Infra/Persistence/Configurations/ExpenseConfiguration.cs @@ -0,0 +1,170 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ERPCore.Infra.Persistence.Configurations; + +/// +/// Expense module tables (docs/EXPENSE_MODULE_PLAN.md §3). The four categories and their +/// titles are seeded as IsSystem rows. The GL codes are the children of the GL +/// "Operating Expenses" (5200) control account created for this module on 2026-10-08 — GL +/// generates codes itself, so a GL instance with a different chart needs these corrected per +/// title on the Expense Setup screen. +/// +public sealed class ExpenseCategoryConfiguration : IEntityTypeConfiguration +{ + public const string UtilityGlCode = "5200-01"; + public const string RentalGlCode = "5200-02"; + public const string SalariesGlCode = "5200-03"; + public const string OtherGlCode = "5200-04"; + + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("expense_categories"); + builder.HasKey(c => c.ExpenseCategoryId); + + builder.Property(c => c.Code).IsRequired().HasMaxLength(30); + builder.HasIndex(c => c.Code).IsUnique(); + builder.Property(c => c.Name).IsRequired().HasMaxLength(100); + builder.HasIndex(c => c.Name).IsUnique(); + + builder.Property(c => c.Status) + .HasConversion().HasMaxLength(20).IsRequired() + .HasDefaultValue(EntityStatus.Active); + builder.Property(c => c.CreatedAt).IsRequired(); + + builder.HasMany(c => c.Titles) + .WithOne(t => t.Category) + .HasForeignKey(t => t.ExpenseCategoryId) + .OnDelete(DeleteBehavior.Restrict); + + var seededAt = new DateTime(2026, 10, 7, 0, 0, 0, DateTimeKind.Utc); + builder.HasData( + new ExpenseCategory { ExpenseCategoryId = 1, Code = "UTILITY", Name = "Utility", IsSystem = true, SortOrder = 1, CreatedAt = seededAt, RowVersion = 1 }, + new ExpenseCategory { ExpenseCategoryId = 2, Code = "RENTAL", Name = "Rental", IsSystem = true, SortOrder = 2, CreatedAt = seededAt, RowVersion = 1 }, + new ExpenseCategory { ExpenseCategoryId = 3, Code = "SALARIES", Name = "Salaries", IsSystem = true, SortOrder = 3, CreatedAt = seededAt, RowVersion = 1 }, + new ExpenseCategory { ExpenseCategoryId = 4, Code = "OTHER", Name = "Other", IsSystem = true, SortOrder = 4, CreatedAt = seededAt, RowVersion = 1 } + ); + } +} + +public sealed class ExpenseTitleConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("expense_titles"); + builder.HasKey(t => t.ExpenseTitleId); + + builder.Property(t => t.Name).IsRequired().HasMaxLength(100); + builder.HasIndex(t => new { t.ExpenseCategoryId, t.Name }).IsUnique(); + builder.Property(t => t.GlExpenseAccountCode).IsRequired().HasMaxLength(20); + + builder.Property(t => t.Status) + .HasConversion().HasMaxLength(20).IsRequired() + .HasDefaultValue(EntityStatus.Active); + builder.Property(t => t.CreatedAt).IsRequired(); + + var seededAt = new DateTime(2026, 10, 7, 0, 0, 0, DateTimeKind.Utc); + var id = 0; + ExpenseTitle Seed(int categoryId, string name, string glCode, int sortOrder) => new() + { + ExpenseTitleId = ++id, ExpenseCategoryId = categoryId, Name = name, GlExpenseAccountCode = glCode, + IsSystem = true, SortOrder = sortOrder, CreatedAt = seededAt, RowVersion = 1 + }; + + // Append only — the ids are assigned in this order, so reordering would renumber seeded rows. + builder.HasData( + Seed(1, "Electricity", ExpenseCategoryConfiguration.UtilityGlCode, 1), + Seed(1, "Water", ExpenseCategoryConfiguration.UtilityGlCode, 2), + Seed(1, "Telephone", ExpenseCategoryConfiguration.UtilityGlCode, 3), + Seed(1, "Internet", ExpenseCategoryConfiguration.UtilityGlCode, 4), + Seed(1, "Gas", ExpenseCategoryConfiguration.UtilityGlCode, 5), + Seed(2, "Building Rent", ExpenseCategoryConfiguration.RentalGlCode, 1), + Seed(2, "Vehicle Rent", ExpenseCategoryConfiguration.RentalGlCode, 2), + Seed(2, "Equipment Rent", ExpenseCategoryConfiguration.RentalGlCode, 3), + Seed(3, "Staff Salaries", ExpenseCategoryConfiguration.SalariesGlCode, 1), + Seed(3, "Wages", ExpenseCategoryConfiguration.SalariesGlCode, 2), + Seed(3, "Overtime", ExpenseCategoryConfiguration.SalariesGlCode, 3), + Seed(3, "Bonus", ExpenseCategoryConfiguration.SalariesGlCode, 4), + Seed(3, "EPF / ETF Contribution", ExpenseCategoryConfiguration.SalariesGlCode, 5), + Seed(4, "Stationery", ExpenseCategoryConfiguration.OtherGlCode, 1), + Seed(4, "Transport", ExpenseCategoryConfiguration.OtherGlCode, 2), + Seed(4, "Repairs & Maintenance", ExpenseCategoryConfiguration.OtherGlCode, 3), + Seed(4, "Cleaning", ExpenseCategoryConfiguration.OtherGlCode, 4), + Seed(4, "Miscellaneous", ExpenseCategoryConfiguration.OtherGlCode, 5) + ); + } +} + +public sealed class ExpenseConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("expenses", t => + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0")); + builder.HasKey(x => x.ExpenseId); + + builder.Property(x => x.DocNo).IsRequired().HasMaxLength(30); + builder.HasIndex(x => x.DocNo).IsUnique(); + + builder.Property(x => x.ExpenseDate).IsRequired(); + builder.Property(x => x.Payee).HasMaxLength(150); + builder.Property(x => x.BillReference).HasMaxLength(50); + builder.Property(x => x.Description).HasMaxLength(500); + builder.Property(x => x.PeriodMonth).HasMaxLength(7); + + foreach (var p in new[] { nameof(Expense.Amount), nameof(Expense.PaidAmount), nameof(Expense.BalanceAmount) }) + builder.Property(p).HasPrecision(18, 4); + + builder.Property(x => x.Status) + .HasConversion().HasMaxLength(20).IsRequired() + .HasDefaultValue(ExpenseStatus.Draft); + builder.Property(x => x.PaymentStatus) + .HasConversion().HasMaxLength(20).IsRequired() + .HasDefaultValue(ExpensePaymentStatus.Unpaid); + + builder.Property(x => x.GlExpenseAccountCode).HasMaxLength(20); + builder.Property(x => x.GlJournalNo).HasMaxLength(30); + builder.Property(x => x.GlReversalJournalNo).HasMaxLength(30); + builder.Property(x => x.CancelReason).HasMaxLength(250); + builder.Property(x => x.CreatedAt).IsRequired(); + + builder.HasOne(x => x.Category).WithMany().HasForeignKey(x => x.ExpenseCategoryId).OnDelete(DeleteBehavior.Restrict); + builder.HasOne(x => x.Title).WithMany().HasForeignKey(x => x.ExpenseTitleId).OnDelete(DeleteBehavior.Restrict); + builder.HasOne(x => x.Vendor).WithMany().HasForeignKey(x => x.VendorId).OnDelete(DeleteBehavior.Restrict); + builder.HasOne(x => x.Branch).WithMany().HasForeignKey(x => x.BranchId).OnDelete(DeleteBehavior.Restrict); + builder.HasOne(x => x.Creator).WithMany().HasForeignKey(x => x.CreatedBy).OnDelete(DeleteBehavior.Restrict); + + builder.HasMany(x => x.Payments) + .WithOne(p => p.Expense) + .HasForeignKey(p => p.ExpenseId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasIndex(x => x.Status); + builder.HasIndex(x => x.PaymentStatus); + builder.HasIndex(x => x.ExpenseDate); + builder.HasIndex(x => x.BranchId); + } +} + +public sealed class ExpensePaymentConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("expense_payments"); + builder.HasKey(p => p.ExpensePaymentId); + + builder.Property(p => p.Amount).HasPrecision(18, 4); + builder.Property(p => p.PaymentDate).IsRequired(); + builder.Property(p => p.PaymentMethod).HasConversion().HasMaxLength(20).IsRequired(); + builder.Property(p => p.BankAccountName).IsRequired().HasMaxLength(200); + builder.Property(p => p.ChequeNo).HasMaxLength(30); + builder.Property(p => p.Reference).HasMaxLength(100); + builder.Property(p => p.GlJournalNo).HasMaxLength(30); + builder.Property(p => p.CreatedAt).IsRequired(); + + builder.HasOne(p => p.Creator).WithMany().HasForeignKey(p => p.CreatedBy).OnDelete(DeleteBehavior.Restrict); + builder.HasIndex(p => p.ExpenseId); + } +} diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs index 843a3b8..b7dacac 100644 --- a/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs +++ b/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs @@ -84,7 +84,13 @@ public sealed class PermissionConfiguration : IEntityTypeConfiguration p.Amount).HasPrecision(18, 4); builder.Property(p => p.PaymentDate).IsRequired(); builder.Property(p => p.BankAccountName).IsRequired().HasMaxLength(200); + builder.Property(p => p.PaymentMethod) + .HasConversion().HasMaxLength(20).IsRequired() + .HasDefaultValue(SalesPaymentMethod.Cash); builder.Property(p => p.Reference).HasMaxLength(100); builder.Property(p => p.GlJournalNo).HasMaxLength(30); builder.Property(p => p.CreatedAt).IsRequired(); diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/SalesSlipConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/SalesSlipConfiguration.cs index 73e8184..5abd308 100644 --- a/Backend/ERPCore/Infra/Persistence/Configurations/SalesSlipConfiguration.cs +++ b/Backend/ERPCore/Infra/Persistence/Configurations/SalesSlipConfiguration.cs @@ -38,7 +38,7 @@ public sealed class SalesSlipConfiguration : IEntityTypeConfiguration .HasConversion().HasMaxLength(20).IsRequired() .HasDefaultValue(SalesSlipStatus.Draft); - foreach (var p in new[] { nameof(SalesSlip.Subtotal), nameof(SalesSlip.DiscountTotal), nameof(SalesSlip.TaxTotal), nameof(SalesSlip.GrandTotal), nameof(SalesSlip.PaidAmount), nameof(SalesSlip.BalanceAmount) }) + foreach (var p in new[] { nameof(SalesSlip.Subtotal), nameof(SalesSlip.DiscountTotal), nameof(SalesSlip.TaxTotal), nameof(SalesSlip.GrandTotal), nameof(SalesSlip.PaidAmount), nameof(SalesSlip.BalanceAmount), nameof(SalesSlip.CashAmount), nameof(SalesSlip.CardAmount) }) builder.Property(p).HasPrecision(18, 4); builder.Property(x => x.CreatedAt).IsRequired(); diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/StockAdjustmentConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/StockAdjustmentConfiguration.cs index 2299db2..e290779 100644 --- a/Backend/ERPCore/Infra/Persistence/Configurations/StockAdjustmentConfiguration.cs +++ b/Backend/ERPCore/Infra/Persistence/Configurations/StockAdjustmentConfiguration.cs @@ -14,9 +14,12 @@ public sealed class StockAdjustmentConfiguration : IEntityTypeConfiguration a.DocNo).IsRequired().HasMaxLength(30); builder.HasIndex(a => a.DocNo).IsUnique(); - builder.Property(a => a.Status).HasConversion().HasMaxLength(20).IsRequired(); + // 30, not 20: "PendingSecondApproval" is 21 characters. + builder.Property(a => a.Status).HasConversion().HasMaxLength(30).IsRequired(); builder.Property(a => a.CreatedAt).IsRequired(); builder.Property(a => a.GlJournalNo).HasMaxLength(30); + builder.Property(a => a.RejectReason).HasMaxLength(250); + builder.HasIndex(a => a.Status); builder.HasOne(a => a.Warehouse).WithMany().HasForeignKey(a => a.WarehouseId).OnDelete(DeleteBehavior.Restrict); builder.HasOne(a => a.ReasonCode).WithMany().HasForeignKey(a => a.ReasonCodeId).OnDelete(DeleteBehavior.Restrict); @@ -35,6 +38,8 @@ public sealed class StockAdjustmentLineConfiguration : IEntityTypeConfiguration< builder.HasKey(l => l.AdjLineId); builder.Property(l => l.QtyDelta).HasPrecision(18, 4); + builder.Property(l => l.RequestedSerialIds).HasMaxLength(4000); + builder.Property(l => l.RequestedSerialNos).HasMaxLength(4000); builder.HasOne(l => l.Adjustment).WithMany(a => a.Lines).HasForeignKey(l => l.AdjustmentId).OnDelete(DeleteBehavior.Cascade); builder.HasOne(l => l.Item).WithMany().HasForeignKey(l => l.ItemId).OnDelete(DeleteBehavior.Restrict); diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/SubNavItemConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/SubNavItemConfiguration.cs index 5ce6c9e..a345cba 100644 --- a/Backend/ERPCore/Infra/Persistence/Configurations/SubNavItemConfiguration.cs +++ b/Backend/ERPCore/Infra/Persistence/Configurations/SubNavItemConfiguration.cs @@ -32,17 +32,20 @@ public sealed class SubNavItemConfiguration : IEntityTypeConfiguration PurchaseInvoiceLines => Set(); public DbSet PurchaseInvoicePayments => Set(); + // --- Accounts: expenses (docs/EXPENSE_MODULE_PLAN.md) --- + public DbSet ExpenseCategories => Set(); + public DbSet ExpenseTitles => Set(); + public DbSet Expenses => Set(); + public DbSet ExpensePayments => Set(); + + // --- Accounts: bank deposits of till cash (docs/BANK_DEPOSIT_MODULE_PLAN.md) --- + public DbSet BankDeposits => Set(); + + // --- Dual approval: stock & price adjustments (docs/DUAL_APPROVAL_PLAN.md) --- + public DbSet DocumentApprovals => Set(); + public DbSet ApprovalConfig => Set(); + public DbSet PriceAdjustments => Set(); + public DbSet PriceAdjustmentLines => Set(); + public DbSet ItemPriceHistory => Set(); + // --- Batch / Serial (docs/10 Part C.4) --- public DbSet Batches => Set(); public DbSet Serials => Set(); @@ -221,6 +237,8 @@ public class ErpDbContext : DbContext modelBuilder.Entity().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId); modelBuilder.Entity().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId); modelBuilder.Entity().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId); + modelBuilder.Entity().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId); + modelBuilder.Entity().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId); // Optimistic concurrency token. On PostgreSQL this was the virtual `xmin` system // column via .IsRowVersion(), which the engine maintained for free. MariaDB has no diff --git a/Backend/ERPCore/Migrations/20261007094000_AddExpenseModule.Designer.cs b/Backend/ERPCore/Migrations/20261007094000_AddExpenseModule.Designer.cs new file mode 100644 index 0000000..2e08d9d --- /dev/null +++ b/Backend/ERPCore/Migrations/20261007094000_AddExpenseModule.Designer.cs @@ -0,0 +1,10153 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261007094000_AddExpenseModule")] + partial class AddExpenseModule + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "6200", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "6200", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "6200", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Day End", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261007094000_AddExpenseModule.cs b/Backend/ERPCore/Migrations/20261007094000_AddExpenseModule.cs new file mode 100644 index 0000000..a0c7891 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261007094000_AddExpenseModule.cs @@ -0,0 +1,336 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace ERPCore.Migrations +{ + /// + public partial class AddExpenseModule : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "expense_categories", + columns: table => new + { + ExpenseCategoryId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Code = table.Column(type: "varchar(30)", maxLength: 30, nullable: false, collation: "utf8mb4_unicode_ci"), + Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: false, collation: "utf8mb4_unicode_ci"), + IsSystem = table.Column(type: "tinyint(1)", nullable: false), + Status = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, defaultValue: "Active", collation: "utf8mb4_unicode_ci"), + SortOrder = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: true), + RowVersion = table.Column(type: "int unsigned", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_expense_categories", x => x.ExpenseCategoryId); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "expense_titles", + columns: table => new + { + ExpenseTitleId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ExpenseCategoryId = table.Column(type: "int", nullable: false), + Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: false, collation: "utf8mb4_unicode_ci"), + GlExpenseAccountCode = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, collation: "utf8mb4_unicode_ci"), + IsSystem = table.Column(type: "tinyint(1)", nullable: false), + Status = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, defaultValue: "Active", collation: "utf8mb4_unicode_ci"), + SortOrder = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: true), + RowVersion = table.Column(type: "int unsigned", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_expense_titles", x => x.ExpenseTitleId); + table.ForeignKey( + name: "FK_expense_titles_expense_categories_ExpenseCategoryId", + column: x => x.ExpenseCategoryId, + principalTable: "expense_categories", + principalColumn: "ExpenseCategoryId", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "expenses", + columns: table => new + { + ExpenseId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + DocNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: false, collation: "utf8mb4_unicode_ci"), + BranchId = table.Column(type: "int", nullable: false), + ExpenseDate = table.Column(type: "date", nullable: false), + DueDate = table.Column(type: "date", nullable: true), + ExpenseCategoryId = table.Column(type: "int", nullable: false), + ExpenseTitleId = table.Column(type: "int", nullable: false), + Payee = table.Column(type: "varchar(150)", maxLength: 150, nullable: true, collation: "utf8mb4_unicode_ci"), + VendorId = table.Column(type: "int", nullable: true), + BillReference = table.Column(type: "varchar(50)", maxLength: 50, nullable: true, collation: "utf8mb4_unicode_ci"), + Description = table.Column(type: "varchar(500)", maxLength: 500, nullable: true, collation: "utf8mb4_unicode_ci"), + PeriodMonth = table.Column(type: "varchar(7)", maxLength: 7, nullable: true, collation: "utf8mb4_unicode_ci"), + Amount = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false), + PaidAmount = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false), + BalanceAmount = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false), + Status = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, defaultValue: "Draft", collation: "utf8mb4_unicode_ci"), + PaymentStatus = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, defaultValue: "Unpaid", collation: "utf8mb4_unicode_ci"), + GlExpenseAccountCode = table.Column(type: "varchar(20)", maxLength: 20, nullable: true, collation: "utf8mb4_unicode_ci"), + GlJournalNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: true, collation: "utf8mb4_unicode_ci"), + GlPostedAt = table.Column(type: "datetime(6)", nullable: true), + GlReversalJournalNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: true, collation: "utf8mb4_unicode_ci"), + CancelReason = table.Column(type: "varchar(250)", maxLength: 250, nullable: true, collation: "utf8mb4_unicode_ci"), + CreatedBy = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: true), + RowVersion = table.Column(type: "int unsigned", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_expenses", x => x.ExpenseId); + table.CheckConstraint("ck_expenses_amount", "`Amount` > 0"); + table.ForeignKey( + name: "FK_expenses_expense_categories_ExpenseCategoryId", + column: x => x.ExpenseCategoryId, + principalTable: "expense_categories", + principalColumn: "ExpenseCategoryId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_expenses_expense_titles_ExpenseTitleId", + column: x => x.ExpenseTitleId, + principalTable: "expense_titles", + principalColumn: "ExpenseTitleId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_expenses_hr_branches_BranchId", + column: x => x.BranchId, + principalTable: "hr_branches", + principalColumn: "BranchId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_expenses_users_CreatedBy", + column: x => x.CreatedBy, + principalTable: "users", + principalColumn: "UserId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_expenses_vendors_VendorId", + column: x => x.VendorId, + principalTable: "vendors", + principalColumn: "VendorId", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "expense_payments", + columns: table => new + { + ExpensePaymentId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ExpenseId = table.Column(type: "int", nullable: false), + Amount = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false), + PaymentDate = table.Column(type: "datetime(6)", nullable: false), + PaymentMethod = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, collation: "utf8mb4_unicode_ci"), + GlBankAccountId = table.Column(type: "bigint", nullable: false), + BankAccountName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false, collation: "utf8mb4_unicode_ci"), + ChequeNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: true, collation: "utf8mb4_unicode_ci"), + Reference = table.Column(type: "varchar(100)", maxLength: 100, nullable: true, collation: "utf8mb4_unicode_ci"), + GlJournalNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: true, collation: "utf8mb4_unicode_ci"), + CreatedBy = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_expense_payments", x => x.ExpensePaymentId); + table.ForeignKey( + name: "FK_expense_payments_expenses_ExpenseId", + column: x => x.ExpenseId, + principalTable: "expenses", + principalColumn: "ExpenseId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_expense_payments_users_CreatedBy", + column: x => x.CreatedBy, + principalTable: "users", + principalColumn: "UserId", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.InsertData( + table: "expense_categories", + columns: new[] { "ExpenseCategoryId", "Code", "CreatedAt", "IsSystem", "Name", "RowVersion", "SortOrder", "UpdatedAt" }, + values: new object[,] + { + { 1, "UTILITY", new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), true, "Utility", 1u, 1, null }, + { 2, "RENTAL", new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), true, "Rental", 1u, 2, null }, + { 3, "SALARIES", new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), true, "Salaries", 1u, 3, null }, + { 4, "OTHER", new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), true, "Other", 1u, 4, null } + }); + + migrationBuilder.InsertData( + table: "sub_nav_items", + columns: new[] { "SubNavItemId", "Code", "Href", "Icon", "Label", "NavItemId", "SortOrder" }, + values: new object[,] + { + { 44, "accounts.expenses", "/dashboard/accounts/expenses", null, "Expenses", 12, 4 }, + { 45, "accounts.expense-setup", "/dashboard/accounts/expense-setup", null, "Expense Setup", 12, 5 } + }); + + migrationBuilder.InsertData( + table: "expense_titles", + columns: new[] { "ExpenseTitleId", "CreatedAt", "ExpenseCategoryId", "GlExpenseAccountCode", "IsSystem", "Name", "RowVersion", "SortOrder", "UpdatedAt" }, + values: new object[,] + { + { 1, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 1, "6100", true, "Electricity", 1u, 1, null }, + { 2, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 1, "6100", true, "Water", 1u, 2, null }, + { 3, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 1, "6100", true, "Telephone", 1u, 3, null }, + { 4, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 1, "6100", true, "Internet", 1u, 4, null }, + { 5, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 1, "6100", true, "Gas", 1u, 5, null }, + { 6, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 2, "6200", true, "Building Rent", 1u, 1, null }, + { 7, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 2, "6200", true, "Vehicle Rent", 1u, 2, null }, + { 8, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 2, "6200", true, "Equipment Rent", 1u, 3, null }, + { 9, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 3, "6300", true, "Staff Salaries", 1u, 1, null }, + { 10, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 3, "6300", true, "Wages", 1u, 2, null }, + { 11, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 3, "6300", true, "Overtime", 1u, 3, null }, + { 12, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 3, "6300", true, "Bonus", 1u, 4, null }, + { 13, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 3, "6300", true, "EPF / ETF Contribution", 1u, 5, null }, + { 14, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 4, "6900", true, "Stationery", 1u, 1, null }, + { 15, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 4, "6900", true, "Transport", 1u, 2, null }, + { 16, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 4, "6900", true, "Repairs & Maintenance", 1u, 3, null }, + { 17, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 4, "6900", true, "Cleaning", 1u, 4, null }, + { 18, new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), 4, "6900", true, "Miscellaneous", 1u, 5, null } + }); + + migrationBuilder.InsertData( + table: "permissions", + columns: new[] { "PermissionId", "Code", "NavItemId", "SubNavItemId" }, + values: new object[,] + { + { 54, "NAV:accounts.expenses", null, 44 }, + { 55, "NAV:accounts.expense-setup", null, 45 } + }); + + migrationBuilder.CreateIndex( + name: "IX_expense_categories_Code", + table: "expense_categories", + column: "Code", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_expense_categories_Name", + table: "expense_categories", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_expense_payments_CreatedBy", + table: "expense_payments", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_expense_payments_ExpenseId", + table: "expense_payments", + column: "ExpenseId"); + + migrationBuilder.CreateIndex( + name: "IX_expense_titles_ExpenseCategoryId_Name", + table: "expense_titles", + columns: new[] { "ExpenseCategoryId", "Name" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_expenses_BranchId", + table: "expenses", + column: "BranchId"); + + migrationBuilder.CreateIndex( + name: "IX_expenses_CreatedBy", + table: "expenses", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_expenses_DocNo", + table: "expenses", + column: "DocNo", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_expenses_ExpenseCategoryId", + table: "expenses", + column: "ExpenseCategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_expenses_ExpenseDate", + table: "expenses", + column: "ExpenseDate"); + + migrationBuilder.CreateIndex( + name: "IX_expenses_ExpenseTitleId", + table: "expenses", + column: "ExpenseTitleId"); + + migrationBuilder.CreateIndex( + name: "IX_expenses_PaymentStatus", + table: "expenses", + column: "PaymentStatus"); + + migrationBuilder.CreateIndex( + name: "IX_expenses_Status", + table: "expenses", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_expenses_VendorId", + table: "expenses", + column: "VendorId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "expense_payments"); + + migrationBuilder.DropTable( + name: "expenses"); + + migrationBuilder.DropTable( + name: "expense_titles"); + + migrationBuilder.DropTable( + name: "expense_categories"); + + migrationBuilder.DeleteData( + table: "permissions", + keyColumn: "PermissionId", + keyValue: 54); + + migrationBuilder.DeleteData( + table: "permissions", + keyColumn: "PermissionId", + keyValue: 55); + + migrationBuilder.DeleteData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 44); + + migrationBuilder.DeleteData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 45); + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008045542_GrantExpensePermissions.Designer.cs b/Backend/ERPCore/Migrations/20261008045542_GrantExpensePermissions.Designer.cs new file mode 100644 index 0000000..b5ffaac --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008045542_GrantExpensePermissions.Designer.cs @@ -0,0 +1,10153 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261008045542_GrantExpensePermissions")] + partial class GrantExpensePermissions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "6100", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "6200", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "6200", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "6200", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "6300", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "6900", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Day End", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008045542_GrantExpensePermissions.cs b/Backend/ERPCore/Migrations/20261008045542_GrantExpensePermissions.cs new file mode 100644 index 0000000..54cfc9c --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008045542_GrantExpensePermissions.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ERPCore.Migrations +{ + /// + /// Data-only: grants the two new Accounts sub-items (NAV:accounts.expenses = 54, + /// NAV:accounts.expense-setup = 55, added by AddExpenseModule) to the Admin role and to every + /// role that already reaches Accounts (NAV:accounts 32, bank-accounts 26, cheque-books 33, + /// received-cheques 34). Without this no role — Admin included — has the codes in + /// /auth/me's navCodes, so the sidebar hides the screens. Admins can still revoke them per + /// role afterwards. INSERT IGNORE keeps it idempotent against grants made by hand. + /// + public partial class GrantExpensePermissions : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(""" + INSERT IGNORE INTO `role_permissions` (`RoleId`, `PermissionId`, `Actions`) + SELECT r.`RoleId`, p.`PermissionId`, 31 + FROM `roles` r + CROSS JOIN (SELECT 54 AS `PermissionId` UNION ALL SELECT 55) p + WHERE LOWER(r.`Code`) = 'admin' + OR EXISTS (SELECT 1 FROM `role_permissions` rp + WHERE rp.`RoleId` = r.`RoleId` AND rp.`PermissionId` IN (26, 32, 33, 34)); + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM `role_permissions` WHERE `PermissionId` IN (54, 55);"); + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008051009_UpdateExpenseGlCodes.Designer.cs b/Backend/ERPCore/Migrations/20261008051009_UpdateExpenseGlCodes.Designer.cs new file mode 100644 index 0000000..1e1fef1 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008051009_UpdateExpenseGlCodes.Designer.cs @@ -0,0 +1,10153 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261008051009_UpdateExpenseGlCodes")] + partial class UpdateExpenseGlCodes + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Day End", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008051009_UpdateExpenseGlCodes.cs b/Backend/ERPCore/Migrations/20261008051009_UpdateExpenseGlCodes.cs new file mode 100644 index 0000000..e4b739c --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008051009_UpdateExpenseGlCodes.cs @@ -0,0 +1,270 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ERPCore.Migrations +{ + /// + public partial class UpdateExpenseGlCodes : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 1, + column: "GlExpenseAccountCode", + value: "5200-01"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 2, + column: "GlExpenseAccountCode", + value: "5200-01"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 3, + column: "GlExpenseAccountCode", + value: "5200-01"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 4, + column: "GlExpenseAccountCode", + value: "5200-01"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 5, + column: "GlExpenseAccountCode", + value: "5200-01"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 6, + column: "GlExpenseAccountCode", + value: "5200-02"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 7, + column: "GlExpenseAccountCode", + value: "5200-02"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 8, + column: "GlExpenseAccountCode", + value: "5200-02"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 9, + column: "GlExpenseAccountCode", + value: "5200-03"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 10, + column: "GlExpenseAccountCode", + value: "5200-03"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 11, + column: "GlExpenseAccountCode", + value: "5200-03"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 12, + column: "GlExpenseAccountCode", + value: "5200-03"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 13, + column: "GlExpenseAccountCode", + value: "5200-03"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 14, + column: "GlExpenseAccountCode", + value: "5200-04"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 15, + column: "GlExpenseAccountCode", + value: "5200-04"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 16, + column: "GlExpenseAccountCode", + value: "5200-04"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 17, + column: "GlExpenseAccountCode", + value: "5200-04"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 18, + column: "GlExpenseAccountCode", + value: "5200-04"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 1, + column: "GlExpenseAccountCode", + value: "6100"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 2, + column: "GlExpenseAccountCode", + value: "6100"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 3, + column: "GlExpenseAccountCode", + value: "6100"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 4, + column: "GlExpenseAccountCode", + value: "6100"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 5, + column: "GlExpenseAccountCode", + value: "6100"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 6, + column: "GlExpenseAccountCode", + value: "6200"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 7, + column: "GlExpenseAccountCode", + value: "6200"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 8, + column: "GlExpenseAccountCode", + value: "6200"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 9, + column: "GlExpenseAccountCode", + value: "6300"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 10, + column: "GlExpenseAccountCode", + value: "6300"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 11, + column: "GlExpenseAccountCode", + value: "6300"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 12, + column: "GlExpenseAccountCode", + value: "6300"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 13, + column: "GlExpenseAccountCode", + value: "6300"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 14, + column: "GlExpenseAccountCode", + value: "6900"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 15, + column: "GlExpenseAccountCode", + value: "6900"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 16, + column: "GlExpenseAccountCode", + value: "6900"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 17, + column: "GlExpenseAccountCode", + value: "6900"); + + migrationBuilder.UpdateData( + table: "expense_titles", + keyColumn: "ExpenseTitleId", + keyValue: 18, + column: "GlExpenseAccountCode", + value: "6900"); + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008055041_RenameDayEndToApprovedInvoices.Designer.cs b/Backend/ERPCore/Migrations/20261008055041_RenameDayEndToApprovedInvoices.Designer.cs new file mode 100644 index 0000000..3ec146a --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008055041_RenameDayEndToApprovedInvoices.Designer.cs @@ -0,0 +1,10153 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261008055041_RenameDayEndToApprovedInvoices")] + partial class RenameDayEndToApprovedInvoices + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Approved Invoices", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008055041_RenameDayEndToApprovedInvoices.cs b/Backend/ERPCore/Migrations/20261008055041_RenameDayEndToApprovedInvoices.cs new file mode 100644 index 0000000..cec3c56 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008055041_RenameDayEndToApprovedInvoices.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ERPCore.Migrations +{ + /// + public partial class RenameDayEndToApprovedInvoices : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 42, + column: "Label", + value: "Approved Invoices"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 42, + column: "Label", + value: "Day End"); + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008061111_AddSalesPaymentMethodsAndDayEndReport.Designer.cs b/Backend/ERPCore/Migrations/20261008061111_AddSalesPaymentMethodsAndDayEndReport.Designer.cs new file mode 100644 index 0000000..ed17921 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008061111_AddSalesPaymentMethodsAndDayEndReport.Designer.cs @@ -0,0 +1,10192 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261008061111_AddSalesPaymentMethodsAndDayEndReport")] + partial class AddSalesPaymentMethodsAndDayEndReport + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }, + new + { + PermissionId = 56, + Code = "NAV:sales.day-end-report", + SubNavItemId = 46 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Cash"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Approved Invoices", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 46, + Code = "sales.day-end-report", + Href = "/dashboard/sales/day-end-report", + Label = "Day-End Report", + NavItemId = 13, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008061111_AddSalesPaymentMethodsAndDayEndReport.cs b/Backend/ERPCore/Migrations/20261008061111_AddSalesPaymentMethodsAndDayEndReport.cs new file mode 100644 index 0000000..9e37a0b --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008061111_AddSalesPaymentMethodsAndDayEndReport.cs @@ -0,0 +1,123 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ERPCore.Migrations +{ + /// + public partial class AddSalesPaymentMethodsAndDayEndReport : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CardAmount", + table: "sales_slips", + type: "decimal(18,4)", + precision: 18, + scale: 4, + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "CashAmount", + table: "sales_slips", + type: "decimal(18,4)", + precision: 18, + scale: 4, + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "PaymentMethod", + table: "sales_invoice_payments", + type: "varchar(20)", + maxLength: 20, + nullable: false, + defaultValue: "Cash", + collation: "utf8mb4_unicode_ci"); + + migrationBuilder.AddColumn( + name: "CardAmount", + table: "bundle_sales", + type: "decimal(18,4)", + precision: 18, + scale: 4, + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "CashAmount", + table: "bundle_sales", + type: "decimal(18,4)", + precision: 18, + scale: 4, + nullable: false, + defaultValue: 0m); + + migrationBuilder.InsertData( + table: "sub_nav_items", + columns: new[] { "SubNavItemId", "Code", "Href", "Icon", "Label", "NavItemId", "SortOrder" }, + values: new object[] { 46, "sales.day-end-report", "/dashboard/sales/day-end-report", null, "Day-End Report", 13, 5 }); + + migrationBuilder.InsertData( + table: "permissions", + columns: new[] { "PermissionId", "Code", "NavItemId", "SubNavItemId" }, + values: new object[] { 56, "NAV:sales.day-end-report", null, 46 }); + + // Every sale before the cash/card split existed was treated as cash (see SalesDayEnd's + // PaidTotal), so existing slips/bundles are all cash; invoice payments already default + // to 'Cash' through the column default above. + migrationBuilder.Sql("UPDATE `sales_slips` SET `CashAmount` = `PaidAmount`, `CardAmount` = 0;"); + migrationBuilder.Sql("UPDATE `bundle_sales` SET `CashAmount` = `GrandTotal`, `CardAmount` = 0;"); + + // Grant the new report (56) to Admin and to every role that already reaches the + // cashier close-out (NAV:sales.day-end = 52) — it shows income, so it follows the + // close-out rather than plain invoicing. Same idempotent pattern as GrantExpensePermissions. + migrationBuilder.Sql(""" + INSERT IGNORE INTO `role_permissions` (`RoleId`, `PermissionId`, `Actions`) + SELECT r.`RoleId`, 56, 31 + FROM `roles` r + WHERE LOWER(r.`Code`) = 'admin' + OR EXISTS (SELECT 1 FROM `role_permissions` rp + WHERE rp.`RoleId` = r.`RoleId` AND rp.`PermissionId` = 52); + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM `role_permissions` WHERE `PermissionId` = 56;"); + + migrationBuilder.DeleteData( + table: "permissions", + keyColumn: "PermissionId", + keyValue: 56); + + migrationBuilder.DeleteData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 46); + + migrationBuilder.DropColumn( + name: "CardAmount", + table: "sales_slips"); + + migrationBuilder.DropColumn( + name: "CashAmount", + table: "sales_slips"); + + migrationBuilder.DropColumn( + name: "PaymentMethod", + table: "sales_invoice_payments"); + + migrationBuilder.DropColumn( + name: "CardAmount", + table: "bundle_sales"); + + migrationBuilder.DropColumn( + name: "CashAmount", + table: "bundle_sales"); + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008072141_AddBankDepositModule.Designer.cs b/Backend/ERPCore/Migrations/20261008072141_AddBankDepositModule.Designer.cs new file mode 100644 index 0000000..f137479 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008072141_AddBankDepositModule.Designer.cs @@ -0,0 +1,10354 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261008072141_AddBankDepositModule")] + partial class AddBankDepositModule + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.Property("BankDepositId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BankDepositId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DepositDate") + .HasColumnType("date"); + + b.Property("DepositSlipNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DepositedBy") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FromCashAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("FromCashAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("FromGlCashAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Posted"); + + b.Property("ToAccountNumber") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ToBankAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ToBankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ToBankName") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("ToGlBankAccountId") + .HasColumnType("bigint"); + + b.HasKey("BankDepositId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ToGlBankAccountId"); + + b.HasIndex("BranchId", "BusinessDate", "Status"); + + b.ToTable("bank_deposits", null, t => + { + t.HasCheckConstraint("ck_bank_deposits_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }, + new + { + PermissionId = 56, + Code = "NAV:sales.day-end-report", + SubNavItemId = 46 + }, + new + { + PermissionId = 57, + Code = "NAV:accounts.bank-deposits", + SubNavItemId = 47 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Cash"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Approved Invoices", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 46, + Code = "sales.day-end-report", + Href = "/dashboard/sales/day-end-report", + Label = "Day-End Report", + NavItemId = 13, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 47, + Code = "accounts.bank-deposits", + Href = "/dashboard/accounts/bank-deposits", + Label = "Bank Deposits", + NavItemId = 12, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261008072141_AddBankDepositModule.cs b/Backend/ERPCore/Migrations/20261008072141_AddBankDepositModule.cs new file mode 100644 index 0000000..351b937 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261008072141_AddBankDepositModule.cs @@ -0,0 +1,131 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ERPCore.Migrations +{ + /// + public partial class AddBankDepositModule : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "bank_deposits", + columns: table => new + { + BankDepositId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + DocNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: false, collation: "utf8mb4_unicode_ci"), + BranchId = table.Column(type: "int", nullable: false), + BusinessDate = table.Column(type: "date", nullable: false), + DepositDate = table.Column(type: "date", nullable: false), + Amount = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false), + FromGlCashAccountId = table.Column(type: "bigint", nullable: false), + FromCashAccountCode = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, collation: "utf8mb4_unicode_ci"), + FromCashAccountName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false, collation: "utf8mb4_unicode_ci"), + ToGlBankAccountId = table.Column(type: "bigint", nullable: false), + ToBankAccountCode = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, collation: "utf8mb4_unicode_ci"), + ToBankAccountName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false, collation: "utf8mb4_unicode_ci"), + ToBankName = table.Column(type: "varchar(150)", maxLength: 150, nullable: true, collation: "utf8mb4_unicode_ci"), + ToAccountNumber = table.Column(type: "varchar(50)", maxLength: 50, nullable: true, collation: "utf8mb4_unicode_ci"), + DepositSlipNo = table.Column(type: "varchar(50)", maxLength: 50, nullable: true, collation: "utf8mb4_unicode_ci"), + DepositedBy = table.Column(type: "varchar(150)", maxLength: 150, nullable: true, collation: "utf8mb4_unicode_ci"), + Remarks = table.Column(type: "varchar(500)", maxLength: 500, nullable: true, collation: "utf8mb4_unicode_ci"), + Status = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, defaultValue: "Posted", collation: "utf8mb4_unicode_ci"), + GlJournalNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: true, collation: "utf8mb4_unicode_ci"), + GlPostedAt = table.Column(type: "datetime(6)", nullable: true), + GlReversalJournalNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: true, collation: "utf8mb4_unicode_ci"), + CancelReason = table.Column(type: "varchar(250)", maxLength: 250, nullable: true, collation: "utf8mb4_unicode_ci"), + CancelledBy = table.Column(type: "int", nullable: true), + CancelledAt = table.Column(type: "datetime(6)", nullable: true), + CreatedBy = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RowVersion = table.Column(type: "int unsigned", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_bank_deposits", x => x.BankDepositId); + table.CheckConstraint("ck_bank_deposits_amount", "`Amount` > 0"); + table.ForeignKey( + name: "FK_bank_deposits_hr_branches_BranchId", + column: x => x.BranchId, + principalTable: "hr_branches", + principalColumn: "BranchId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_bank_deposits_users_CreatedBy", + column: x => x.CreatedBy, + principalTable: "users", + principalColumn: "UserId", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.InsertData( + table: "sub_nav_items", + columns: new[] { "SubNavItemId", "Code", "Href", "Icon", "Label", "NavItemId", "SortOrder" }, + values: new object[] { 47, "accounts.bank-deposits", "/dashboard/accounts/bank-deposits", null, "Bank Deposits", 12, 6 }); + + migrationBuilder.InsertData( + table: "permissions", + columns: new[] { "PermissionId", "Code", "NavItemId", "SubNavItemId" }, + values: new object[] { 57, "NAV:accounts.bank-deposits", null, 47 }); + + migrationBuilder.CreateIndex( + name: "IX_bank_deposits_BranchId_BusinessDate_Status", + table: "bank_deposits", + columns: new[] { "BranchId", "BusinessDate", "Status" }); + + migrationBuilder.CreateIndex( + name: "IX_bank_deposits_CreatedBy", + table: "bank_deposits", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_bank_deposits_DocNo", + table: "bank_deposits", + column: "DocNo", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_bank_deposits_ToGlBankAccountId", + table: "bank_deposits", + column: "ToGlBankAccountId"); + + // Grant the new Accounts sub-item (57) to Admin and to every role that already reaches + // Accounts (NAV:accounts 32, bank-accounts 26, cheque-books 33, received-cheques 34, + // expenses 54). Actions 31 = all, so Create (record) and Delete (cancel) included; admins + // can narrow it per role afterwards. Same idempotent pattern as GrantExpensePermissions. + migrationBuilder.Sql(""" + INSERT IGNORE INTO `role_permissions` (`RoleId`, `PermissionId`, `Actions`) + SELECT r.`RoleId`, 57, 31 + FROM `roles` r + WHERE LOWER(r.`Code`) = 'admin' + OR EXISTS (SELECT 1 FROM `role_permissions` rp + WHERE rp.`RoleId` = r.`RoleId` AND rp.`PermissionId` IN (26, 32, 33, 34, 54)); + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM `role_permissions` WHERE `PermissionId` = 57;"); + + migrationBuilder.DropTable( + name: "bank_deposits"); + + migrationBuilder.DeleteData( + table: "permissions", + keyColumn: "PermissionId", + keyValue: 57); + + migrationBuilder.DeleteData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 47); + } + } +} diff --git a/Backend/ERPCore/Migrations/20261009053644_EnableLowStockAlerts.Designer.cs b/Backend/ERPCore/Migrations/20261009053644_EnableLowStockAlerts.Designer.cs new file mode 100644 index 0000000..6a3e9a4 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261009053644_EnableLowStockAlerts.Designer.cs @@ -0,0 +1,10354 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261009053644_EnableLowStockAlerts")] + partial class EnableLowStockAlerts + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.Property("BankDepositId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BankDepositId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DepositDate") + .HasColumnType("date"); + + b.Property("DepositSlipNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DepositedBy") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FromCashAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("FromCashAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("FromGlCashAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Posted"); + + b.Property("ToAccountNumber") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ToBankAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ToBankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ToBankName") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("ToGlBankAccountId") + .HasColumnType("bigint"); + + b.HasKey("BankDepositId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ToGlBankAccountId"); + + b.HasIndex("BranchId", "BusinessDate", "Status"); + + b.ToTable("bank_deposits", null, t => + { + t.HasCheckConstraint("ck_bank_deposits_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }, + new + { + PermissionId = 56, + Code = "NAV:sales.day-end-report", + SubNavItemId = 46 + }, + new + { + PermissionId = 57, + Code = "NAV:accounts.bank-deposits", + SubNavItemId = 47 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Cash"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Approved Invoices", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 46, + Code = "sales.day-end-report", + Href = "/dashboard/sales/day-end-report", + Label = "Day-End Report", + NavItemId = 13, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 47, + Code = "accounts.bank-deposits", + Href = "/dashboard/accounts/bank-deposits", + Label = "Bank Deposits", + NavItemId = 12, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261009053644_EnableLowStockAlerts.cs b/Backend/ERPCore/Migrations/20261009053644_EnableLowStockAlerts.cs new file mode 100644 index 0000000..46e4e58 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261009053644_EnableLowStockAlerts.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ERPCore.Migrations +{ + /// + /// Data-only: turns the new "stock.low" (Low Stock / Reorder Alert) module on for the in-app + /// bell. A module with no row is silently off, so without this no alert would ever reach anyone + /// until an admin ticked it under Settings → Notifications. Only inserts when the notification + /// config exists and the module has no row yet, so a choice already made is left alone. + /// + public partial class EnableLowStockAlerts : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(""" + INSERT INTO `notification_config_modules` (`NotificationConfigId`, `ModuleKey`, `EmailEnabled`, `SmsEnabled`, `InAppEnabled`) + SELECT c.`NotificationConfigId`, 'stock.low', 0, 0, 1 + FROM `notification_config` c + WHERE c.`NotificationConfigId` = 1 + AND NOT EXISTS (SELECT 1 FROM `notification_config_modules` m WHERE m.`ModuleKey` = 'stock.low'); + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM `notification_config_modules` WHERE `ModuleKey` = 'stock.low';"); + } + } +} diff --git a/Backend/ERPCore/Migrations/20261009070055_AddDualApproval.Designer.cs b/Backend/ERPCore/Migrations/20261009070055_AddDualApproval.Designer.cs new file mode 100644 index 0000000..9e52d83 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261009070055_AddDualApproval.Designer.cs @@ -0,0 +1,10770 @@ +// +using System; +using ERPCore.Infra.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ERPCore.Migrations +{ + [DbContext(typeof(ErpDbContext))] + [Migration("20261009070055_AddDualApproval")] + partial class AddDualApproval + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_unicode_ci") + .HasAnnotation("ProductVersion", "9.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("ERPCore.Domain.Entities.ApprovalConfig", b => + { + b.Property("ApprovalConfigId") + .HasColumnType("int"); + + b.Property("Level2RoleCodes") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PriceAdjustmentLevel2ThresholdPercent") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("PriceAdjustmentLevels") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StockAdjustmentLevel2ThresholdValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StockAdjustmentLevels") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ApprovalConfigId"); + + b.ToTable("approval_config", null, t => + { + t.HasCheckConstraint("ck_approval_config_singleton", "`ApprovalConfigId` = 1"); + }); + + b.HasData( + new + { + ApprovalConfigId = 1, + PriceAdjustmentLevel2ThresholdPercent = 0m, + PriceAdjustmentLevels = 2, + RowVersion = 1u, + StockAdjustmentLevel2ThresholdValue = 0m, + StockAdjustmentLevels = 2 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.Property("AttendanceRecordId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId")); + + b.Property("AttendanceDate") + .HasColumnType("datetime(6)"); + + b.Property("AttendanceStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("AttendanceUploadBatchId") + .HasColumnType("int"); + + b.Property("CheckIn") + .HasColumnType("time(6)"); + + b.Property("CheckOut") + .HasColumnType("time(6)"); + + b.Property("DuplicateOfAttendanceRecordId") + .HasColumnType("int"); + + b.Property("EarlyLeaveMinutes") + .HasColumnType("int"); + + b.Property("EditedAt") + .HasColumnType("datetime(6)"); + + b.Property("EditedBy") + .HasColumnType("int"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsManualOverride") + .HasColumnType("tinyint(1)"); + + b.Property("LateMinutes") + .HasColumnType("int"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OvertimeMinutes") + .HasColumnType("int"); + + b.Property("RowValidationStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.Property("WorkingMinutes") + .HasColumnType("int"); + + b.HasKey("AttendanceRecordId"); + + b.HasIndex("AttendanceUploadBatchId"); + + b.HasIndex("WorkShiftId"); + + b.HasIndex("EmployeeId", "AttendanceDate"); + + b.ToTable("hr_attendance_records", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b => + { + b.Property("AttendanceUploadBatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId")); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("PeriodEnd") + .HasColumnType("datetime(6)"); + + b.Property("PeriodStart") + .HasColumnType("datetime(6)"); + + b.Property("RowCountDuplicate") + .HasColumnType("int"); + + b.Property("RowCountError") + .HasColumnType("int"); + + b.Property("RowCountTotal") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("AttendanceUploadBatchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodStart", "PeriodEnd"); + + b.ToTable("hr_attendance_upload_batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("ChangeSet") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("AuditId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_logs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.Property("BankDepositId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BankDepositId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DepositDate") + .HasColumnType("date"); + + b.Property("DepositSlipNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DepositedBy") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FromCashAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("FromCashAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("FromGlCashAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Posted"); + + b.Property("ToAccountNumber") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ToBankAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ToBankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ToBankName") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("ToGlBankAccountId") + .HasColumnType("bigint"); + + b.HasKey("BankDepositId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ToGlBankAccountId"); + + b.HasIndex("BranchId", "BusinessDate", "Status"); + + b.ToTable("bank_deposits", null, t => + { + t.HasCheckConstraint("ck_bank_deposits_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.Property("BatchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BatchNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.HasKey("BatchId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("ItemId", "BatchNo") + .IsUnique(); + + b.ToTable("batches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.Property("BinId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId")); + + b.Property("BinType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BinId"); + + b.HasIndex("WarehouseId", "Code") + .IsUnique(); + + b.ToTable("bins", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b => + { + b.Property("BranchId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId")); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_branches", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BranchingEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarehouseBranchingEnabled") + .HasColumnType("tinyint(1)"); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("branch_config", null, t => + { + t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b => + { + b.Property("BrandId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BrandId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("brands", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Property("BundleSaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId")); + + b.Property("BundleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundleDate") + .HasColumnType("datetime(6)"); + + b.Property("BundleName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BundleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BundlePrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ComponentSubtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("MarginAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleId"); + + b.HasIndex("BundleNo") + .IsUnique(); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sales", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.Property("BundleSaleLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId")); + + b.Property("BundleSaleId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IncludeInBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsComponent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleLineId"); + + b.HasIndex("BundleSaleId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Property("BundleSaleTemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TemplateCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("BundleSaleTemplateId"); + + b.HasIndex("TemplateCode") + .IsUnique(); + + b.ToTable("bundle_sale_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.Property("BundleSaleTemplateLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId")); + + b.Property("BundleSaleTemplateId") + .HasColumnType("int"); + + b.Property("IncludeInBundle") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("BundleSaleTemplateLineId"); + + b.HasIndex("BundleSaleTemplateId"); + + b.HasIndex("ItemId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("bundle_sale_template_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CategoryId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("categories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.Property("CustomerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId")); + + b.Property("AddressLine1") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("AddressLine2") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditDays") + .HasColumnType("int"); + + b.Property("CreditLimit") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CustomerCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CustomerType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxRegistrationNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("CustomerId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomerCode") + .IsUnique(); + + b.HasIndex("CustomerType"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("customers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.Property("DepartmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("HeadEmployeeId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ParentDepartmentId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DepartmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("HeadEmployeeId"); + + b.HasIndex("ParentDepartmentId"); + + b.HasIndex("Status"); + + b.ToTable("hr_departments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b => + { + b.Property("DesignationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("DesignationId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_designations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.DocumentApproval", b => + { + b.Property("DocumentApprovalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DocumentApprovalId")); + + b.Property("Comment") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("DecidedAt") + .HasColumnType("datetime(6)"); + + b.Property("DecidedBy") + .HasColumnType("int"); + + b.Property("Decision") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DocId") + .HasColumnType("int"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Level") + .HasColumnType("int"); + + b.HasKey("DocumentApprovalId"); + + b.HasIndex("DecidedBy"); + + b.HasIndex("DocType", "DocId"); + + b.ToTable("document_approvals", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.Property("EmployeeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId")); + + b.Property("AddressLine1") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AddressLine2") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ConfirmationDate") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DateOfBirth") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("DesignationId") + .HasColumnType("int"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmergencyContactRelationship") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("EmployeeCode") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmploymentTypeId") + .HasColumnType("int"); + + b.Property("EpfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EtfNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("HireDate") + .HasColumnType("datetime(6)"); + + b.Property("LastWorkingDate") + .HasColumnType("datetime(6)"); + + b.Property("Nationality") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Nic") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PersonalMobile") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PostalCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ProfilePhotoPath") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReportingManagerId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxIdentificationNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WorkShiftId") + .HasColumnType("int"); + + b.HasKey("EmployeeId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("DesignationId"); + + b.HasIndex("Email"); + + b.HasIndex("EmployeeCode") + .IsUnique(); + + b.HasIndex("EmploymentTypeId"); + + b.HasIndex("ReportingManagerId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("WorkShiftId"); + + b.ToTable("hr_employees", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.Property("EmployeeBankDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeBankDetailId")); + + b.Property("AccountHolderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BankName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("BranchName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("IsPrimary") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("SwiftCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmployeeBankDetailId"); + + b.HasIndex("EmployeeId"); + + b.ToTable("hr_employee_bank_details", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.Property("EmployeeDocumentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeDocumentId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("HrDocumentTypeId") + .HasColumnType("int"); + + b.Property("IssueDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.Property("VerifiedAt") + .HasColumnType("datetime(6)"); + + b.Property("VerifiedBy") + .HasColumnType("int"); + + b.HasKey("EmployeeDocumentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("HrDocumentTypeId"); + + b.ToTable("hr_employee_documents", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Property("EmployeeLoanId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeLoanId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("InstallmentAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("InterestRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("LoanKind") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("NumberOfInstallments") + .HasColumnType("int"); + + b.Property("OutstandingBalance") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PrincipalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartMonth") + .HasColumnType("int"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeLoanId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("hr_employee_loans", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Property("EmployeeSalaryStructureId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("varchar(3)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("EmployeeSalaryStructureId"); + + b.HasIndex("EmployeeId", "EffectiveTo"); + + b.ToTable("hr_employee_salary_structures", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.Property("EmployeeSalaryStructureLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeSalaryStructureLineId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeSalaryStructureId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.HasKey("EmployeeSalaryStructureLineId"); + + b.HasIndex("EmployeeSalaryStructureId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_employee_salary_structure_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmploymentType", b => + { + b.Property("EmploymentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmploymentTypeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("EmploymentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_employment_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Property("ExpenseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BillReference") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("ExpenseDate") + .HasColumnType("date"); + + b.Property("ExpenseTitleId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Payee") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PaymentStatus") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Unpaid"); + + b.Property("PeriodMonth") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("ExpenseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ExpenseCategoryId"); + + b.HasIndex("ExpenseDate"); + + b.HasIndex("ExpenseTitleId"); + + b.HasIndex("PaymentStatus"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("expenses", null, t => + { + t.HasCheckConstraint("ck_expenses_amount", "`Amount` > 0"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Property("ExpenseCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseCategoryId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseCategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("expense_categories", (string)null); + + b.HasData( + new + { + ExpenseCategoryId = 1, + Code = "UTILITY", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Utility", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseCategoryId = 2, + Code = "RENTAL", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Rental", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseCategoryId = 3, + Code = "SALARIES", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Salaries", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseCategoryId = 4, + Code = "OTHER", + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + IsSystem = true, + Name = "Other", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.Property("ExpensePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpensePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ChequeNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("ExpenseId") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("ExpensePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ExpenseId"); + + b.ToTable("expense_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.Property("ExpenseTitleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ExpenseTitleId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpenseCategoryId") + .HasColumnType("int"); + + b.Property("GlExpenseAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ExpenseTitleId"); + + b.HasIndex("ExpenseCategoryId", "Name") + .IsUnique(); + + b.ToTable("expense_titles", (string)null); + + b.HasData( + new + { + ExpenseTitleId = 1, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Electricity", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 2, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Water", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 3, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Telephone", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 4, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Internet", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 5, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 1, + GlExpenseAccountCode = "5200-01", + IsSystem = true, + Name = "Gas", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 6, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Building Rent", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 7, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Vehicle Rent", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 8, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 2, + GlExpenseAccountCode = "5200-02", + IsSystem = true, + Name = "Equipment Rent", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 9, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Staff Salaries", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 10, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Wages", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 11, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Overtime", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 12, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "Bonus", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 13, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 3, + GlExpenseAccountCode = "5200-03", + IsSystem = true, + Name = "EPF / ETF Contribution", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }, + new + { + ExpenseTitleId = 14, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Stationery", + RowVersion = 1u, + SortOrder = 1, + Status = "Active" + }, + new + { + ExpenseTitleId = 15, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Transport", + RowVersion = 1u, + SortOrder = 2, + Status = "Active" + }, + new + { + ExpenseTitleId = 16, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Repairs & Maintenance", + RowVersion = 1u, + SortOrder = 3, + Status = "Active" + }, + new + { + ExpenseTitleId = 17, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Cleaning", + RowVersion = 1u, + SortOrder = 4, + Status = "Active" + }, + new + { + ExpenseTitleId = 18, + CreatedAt = new DateTime(2026, 10, 7, 0, 0, 0, 0, DateTimeKind.Utc), + ExpenseCategoryId = 4, + GlExpenseAccountCode = "5200-04", + IsSystem = true, + Name = "Miscellaneous", + RowVersion = 1u, + SortOrder = 5, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Property("GrnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("GrnId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("PoId"); + + b.HasIndex("PurchaseInvoiceId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("grns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowVat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("grn_config", null, t => + { + t.HasCheckConstraint("ck_grn_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Property("GrnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("HoldStatus") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PoLineId") + .HasColumnType("int"); + + b.Property("PoUnitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceivedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("VatAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.HasKey("GrnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("GrnId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoLineId"); + + b.ToTable("grn_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.Property("GrnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineSerialId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("GrnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("GrnLineId", "SerialNo") + .IsUnique(); + + b.ToTable("grn_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.Property("GrnLineWarrantyNumberId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("GrnLineWarrantyNumberId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("WarrantyNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("GrnLineWarrantyNumberId"); + + b.HasIndex("GrnLineId", "WarrantyNo") + .IsUnique(); + + b.ToTable("grn_line_warranty_numbers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.HrDocumentType", b => + { + b.Property("HrDocumentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("HrDocumentTypeId")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryTracked") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiredAtOnboarding") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("HrDocumentTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_document_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Property("ItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BaseUomId") + .HasColumnType("int"); + + b.Property("BrandId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ContentBaseQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentBaseUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContentQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ContentUnit") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultVendorId") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SellingMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Sku") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("StockNature") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SubCategoryId") + .HasColumnType("int"); + + b.Property("TaxRateId") + .HasColumnType("int"); + + b.Property("TrackingMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Warranty") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("NonWarranty"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("ItemId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BaseUomId"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DefaultVendorId"); + + b.HasIndex("Sku") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SubCategoryId"); + + b.HasIndex("TaxRateId"); + + b.ToTable("items", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.Property("ItemImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemImageId")); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("RelativePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("varchar(260)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.Property("UploadedBy") + .HasColumnType("int"); + + b.HasKey("ItemImageId"); + + b.HasIndex("ItemId", "SortOrder"); + + b.ToTable("item_images", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemPriceHistory", b => + { + b.Property("ItemPriceHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemPriceHistoryId")); + + b.Property("AppliedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1By") + .HasColumnType("int"); + + b.Property("ApprovedLevel2By") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("NewFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PriceAdjustmentId") + .HasColumnType("int"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.HasKey("ItemPriceHistoryId"); + + b.HasIndex("PriceAdjustmentId"); + + b.HasIndex("ItemId", "AppliedAt"); + + b.ToTable("item_price_history", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.Property("ReorderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReorderId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReorderQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReorderId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId") + .IsUnique(); + + b.ToTable("item_reorders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b => + { + b.Property("ItemTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemTypeId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsMeasurable") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("ItemTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("item_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b => + { + b.Property("JournalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("JournalId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CreditAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DebitAccount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.HasKey("JournalId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("journal_entry_stubs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.Property("LeaveBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveBalanceId")); + + b.Property("AdjustmentDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarriedForwardDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EntitledDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TakenDays") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("LeaveBalanceId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("EmployeeId", "LeaveTypeId", "Year") + .IsUnique(); + + b.ToTable("hr_leave_balances", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.Property("LeaveRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveRequestId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DaysCount") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveTypeId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RejectionReason") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LeaveRequestId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveTypeId"); + + b.HasIndex("Status"); + + b.HasIndex("StartDate", "EndDate"); + + b.ToTable("hr_leave_requests", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveType", b => + { + b.Property("LeaveTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LeaveTypeId")); + + b.Property("AccrualPerYear") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("CarryForwardAllowed") + .HasColumnType("tinyint(1)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CountsAsNoPay") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPaid") + .HasColumnType("tinyint(1)"); + + b.Property("MaxCarryForwardDays") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequiresApproval") + .HasColumnType("tinyint(1)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("LeaveTypeId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_leave_types", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.Property("LoanInstallmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoanInstallmentId")); + + b.Property("DueMonth") + .HasColumnType("int"); + + b.Property("DueYear") + .HasColumnType("int"); + + b.Property("EmployeeLoanId") + .HasColumnType("int"); + + b.Property("InstallmentNumber") + .HasColumnType("int"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScheduledAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("LoanInstallmentId"); + + b.HasIndex("EmployeeLoanId"); + + b.HasIndex("PayrollRunId"); + + b.HasIndex("DueYear", "DueMonth"); + + b.ToTable("hr_loan_installments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.Property("LoginActivityId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LoginActivityId")); + + b.Property("DeviceLabel") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LoginAt") + .HasColumnType("datetime(6)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginActivityId"); + + b.HasIndex("UserId", "LoginAt"); + + b.ToTable("login_activities", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Property("NavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("NavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("nav_items", (string)null); + + b.HasData( + new + { + NavItemId = 1, + Code = "dashboard", + Href = "/dashboard", + Label = "Dashboard", + SortOrder = 1, + Status = "Active" + }, + new + { + NavItemId = 2, + Code = "products", + Href = "/dashboard/products", + Label = "Products", + SortOrder = 2, + Status = "Active" + }, + new + { + NavItemId = 4, + Code = "procurement", + Href = "/dashboard/procurement", + Label = "Procurement", + SortOrder = 4, + Status = "Active" + }, + new + { + NavItemId = 5, + Code = "receiving", + Href = "/dashboard/receiving/grn", + Label = "Receiving", + SortOrder = 5, + Status = "Active" + }, + new + { + NavItemId = 6, + Code = "stock", + Href = "/dashboard/stock", + Label = "Stock", + SortOrder = 6, + Status = "Active" + }, + new + { + NavItemId = 7, + Code = "warehouses", + Href = "/dashboard/warehouse", + Label = "Warehouses", + SortOrder = 7, + Status = "Active" + }, + new + { + NavItemId = 8, + Code = "orders", + Href = "/dashboard/orders", + Label = "Orders", + SortOrder = 8, + Status = "Active" + }, + new + { + NavItemId = 9, + Code = "settings", + Href = "/dashboard/settings", + Label = "Settings", + SortOrder = 9, + Status = "Active" + }, + new + { + NavItemId = 10, + Code = "help", + Href = "/dashboard/help", + Label = "Help", + SortOrder = 10, + Status = "Active" + }, + new + { + NavItemId = 11, + Code = "ledgers", + Href = "/dashboard/ledgers", + Label = "Ledgers", + SortOrder = 11, + Status = "Active" + }, + new + { + NavItemId = 12, + Code = "accounts", + Href = "/dashboard/accounts", + Label = "Accounts", + SortOrder = 12, + Status = "Active" + }, + new + { + NavItemId = 13, + Code = "sales", + Href = "/dashboard/sales", + Label = "Sales", + SortOrder = 13, + Status = "Active" + }, + new + { + NavItemId = 14, + Code = "production", + Href = "/dashboard/production", + Label = "Production", + SortOrder = 14, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Property("NotificationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RelatedEntityId") + .HasColumnType("int"); + + b.Property("RelatedEntityType") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("NotificationId"); + + b.HasIndex("CreatedAt"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("NotificationConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("notification_config", null, t => + { + t.HasCheckConstraint("ck_notification_config_singleton", "`NotificationConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.Property("NotificationConfigModuleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationConfigModuleId")); + + b.Property("EmailEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("InAppEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ModuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NotificationConfigId") + .HasColumnType("int"); + + b.Property("SmsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.HasKey("NotificationConfigModuleId"); + + b.HasIndex("ModuleKey") + .IsUnique(); + + b.HasIndex("NotificationConfigId"); + + b.ToTable("notification_config_modules", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.Property("NotificationRecipientId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("NotificationRecipientId")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("ReadAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("NotificationRecipientId"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId", "IsRead", "NotificationId"); + + b.ToTable("notification_recipients", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SequenceId")); + + b.Property("BranchId") + .HasColumnType("int") + .HasColumnName("branch_id"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)") + .HasColumnName("doc_type"); + + b.Property("LastNumber") + .HasColumnType("int") + .HasColumnName("last_number"); + + b.Property("Year") + .HasColumnType("int") + .HasColumnName("year"); + + b.HasKey("SequenceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocType", "BranchId", "Year") + .IsUnique(); + + b.ToTable("number_sequences", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Property("PayrollLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineId")); + + b.Property("AbsentDays") + .HasColumnType("int"); + + b.Property("BasicSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployeeId") + .HasColumnType("int"); + + b.Property("EpfEmployeeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EpfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EtfEmployerAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("GrossSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("LateMinutesTotal") + .HasColumnType("int"); + + b.Property("LeaveDays") + .HasColumnType("int"); + + b.Property("LoanDeductionAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetSalary") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NoPayAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OtMinutesTotal") + .HasColumnType("int"); + + b.Property("OtherDeductionsAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("OvertimeAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollRunId") + .HasColumnType("int"); + + b.Property("PresentDays") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("TotalAllowances") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("WorkingDays") + .HasColumnType("int"); + + b.HasKey("PayrollLineId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollRunId", "EmployeeId") + .IsUnique(); + + b.ToTable("hr_payroll_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.Property("PayrollLineComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollLineComponentId")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ComponentCategory") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("SalaryComponentId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("PayrollLineComponentId"); + + b.HasIndex("PayrollLineId"); + + b.HasIndex("SalaryComponentId"); + + b.ToTable("hr_payroll_line_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Property("PayrollRunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollRunId")); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("GeneratedBy") + .HasColumnType("int"); + + b.Property("LockedAt") + .HasColumnType("datetime(6)"); + + b.Property("LockedBy") + .HasColumnType("int"); + + b.Property("PeriodMonth") + .HasColumnType("int"); + + b.Property("PeriodYear") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UnlockReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UnlockedAt") + .HasColumnType("datetime(6)"); + + b.Property("UnlockedBy") + .HasColumnType("int"); + + b.HasKey("PayrollRunId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("PeriodYear", "PeriodMonth", "BranchId"); + + b.ToTable("hr_payroll_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollStatutorySetting", b => + { + b.Property("PayrollStatutorySettingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayrollStatutorySettingId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("EpfEmployeeRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EpfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("EtfEmployerRate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("OtMultiplierDefault") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.HasKey("PayrollStatutorySettingId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_payroll_statutory_settings", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.Property("PayslipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PayslipId")); + + b.Property("GeneratedAt") + .HasColumnType("datetime(6)"); + + b.Property("PayrollLineId") + .HasColumnType("int"); + + b.Property("ReleasedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedBy") + .HasColumnType("int"); + + b.HasKey("PayslipId"); + + b.HasIndex("PayrollLineId") + .IsUnique(); + + b.ToTable("hr_payslips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PermissionId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SubNavItemId") + .HasColumnType("int"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.HasIndex("SubNavItemId"); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + PermissionId = 1, + Code = "NAV:dashboard", + NavItemId = 1 + }, + new + { + PermissionId = 2, + Code = "NAV:products", + NavItemId = 2 + }, + new + { + PermissionId = 3, + Code = "NAV:procurement.vendors", + SubNavItemId = 30 + }, + new + { + PermissionId = 4, + Code = "NAV:procurement", + NavItemId = 4 + }, + new + { + PermissionId = 5, + Code = "NAV:receiving", + NavItemId = 5 + }, + new + { + PermissionId = 6, + Code = "NAV:stock", + NavItemId = 6 + }, + new + { + PermissionId = 7, + Code = "NAV:warehouses", + NavItemId = 7 + }, + new + { + PermissionId = 8, + Code = "NAV:orders", + NavItemId = 8 + }, + new + { + PermissionId = 9, + Code = "NAV:settings", + NavItemId = 9 + }, + new + { + PermissionId = 10, + Code = "NAV:help", + NavItemId = 10 + }, + new + { + PermissionId = 11, + Code = "NAV:products.item", + SubNavItemId = 1 + }, + new + { + PermissionId = 12, + Code = "NAV:products.category", + SubNavItemId = 2 + }, + new + { + PermissionId = 13, + Code = "NAV:products.brand", + SubNavItemId = 3 + }, + new + { + PermissionId = 14, + Code = "NAV:products.item-type", + SubNavItemId = 4 + }, + new + { + PermissionId = 15, + Code = "NAV:products.uom", + SubNavItemId = 5 + }, + new + { + PermissionId = 16, + Code = "NAV:products.configuration", + SubNavItemId = 6 + }, + new + { + PermissionId = 17, + Code = "NAV:settings.roles", + SubNavItemId = 7 + }, + new + { + PermissionId = 18, + Code = "NAV:settings.users", + SubNavItemId = 8 + }, + new + { + PermissionId = 28, + Code = "NAV:procurement.requisitions", + SubNavItemId = 17 + }, + new + { + PermissionId = 29, + Code = "NAV:procurement.rfqs", + SubNavItemId = 18 + }, + new + { + PermissionId = 30, + Code = "NAV:procurement.purchase-orders", + SubNavItemId = 19 + }, + new + { + PermissionId = 31, + Code = "NAV:procurement.purchase-returns", + SubNavItemId = 20 + }, + new + { + PermissionId = 19, + Code = "NAV:ledgers", + NavItemId = 11 + }, + new + { + PermissionId = 20, + Code = "NAV:ledgers.trial-balance", + SubNavItemId = 9 + }, + new + { + PermissionId = 21, + Code = "NAV:ledgers.balance-sheet", + SubNavItemId = 10 + }, + new + { + PermissionId = 22, + Code = "NAV:ledgers.general-ledger", + SubNavItemId = 11 + }, + new + { + PermissionId = 23, + Code = "NAV:ledgers.profit-and-loss", + SubNavItemId = 12 + }, + new + { + PermissionId = 24, + Code = "NAV:ledgers.cash-flow", + SubNavItemId = 13 + }, + new + { + PermissionId = 25, + Code = "NAV:ledgers.budget-vs-actual", + SubNavItemId = 14 + }, + new + { + PermissionId = 27, + Code = "NAV:ledgers.tax-report", + SubNavItemId = 16 + }, + new + { + PermissionId = 26, + Code = "NAV:accounts.bank-accounts", + SubNavItemId = 15 + }, + new + { + PermissionId = 32, + Code = "NAV:accounts", + NavItemId = 12 + }, + new + { + PermissionId = 33, + Code = "NAV:accounts.cheque-books", + SubNavItemId = 21 + }, + new + { + PermissionId = 34, + Code = "NAV:accounts.received-cheques", + SubNavItemId = 22 + }, + new + { + PermissionId = 35, + Code = "NAV:production", + NavItemId = 14 + }, + new + { + PermissionId = 36, + Code = "NAV:production.templates", + SubNavItemId = 27 + }, + new + { + PermissionId = 37, + Code = "NAV:production.runs", + SubNavItemId = 25 + }, + new + { + PermissionId = 38, + Code = "NAV:production.produced", + SubNavItemId = 26 + }, + new + { + PermissionId = 39, + Code = "NAV:procurement.purchase-invoices", + SubNavItemId = 28 + }, + new + { + PermissionId = 40, + Code = "NAV:settings.branches", + SubNavItemId = 29 + }, + new + { + PermissionId = 41, + Code = "NAV:stock.ledger", + SubNavItemId = 31 + }, + new + { + PermissionId = 42, + Code = "NAV:stock.enquiry", + SubNavItemId = 32 + }, + new + { + PermissionId = 43, + Code = "NAV:stock.valuation", + SubNavItemId = 33 + }, + new + { + PermissionId = 44, + Code = "NAV:stock.transfers", + SubNavItemId = 34 + }, + new + { + PermissionId = 45, + Code = "NAV:stock.adjustments", + SubNavItemId = 35 + }, + new + { + PermissionId = 46, + Code = "NAV:stock.counts", + SubNavItemId = 36 + }, + new + { + PermissionId = 47, + Code = "NAV:stock.reorder-alerts", + SubNavItemId = 37 + }, + new + { + PermissionId = 48, + Code = "NAV:stock.wastage", + SubNavItemId = 38 + }, + new + { + PermissionId = 49, + Code = "NAV:sales.bundle-templates", + SubNavItemId = 23 + }, + new + { + PermissionId = 50, + Code = "NAV:sales.invoices", + SubNavItemId = 40 + }, + new + { + PermissionId = 51, + Code = "NAV:sales.free-issues", + SubNavItemId = 41 + }, + new + { + PermissionId = 52, + Code = "NAV:sales.day-end", + SubNavItemId = 42 + }, + new + { + PermissionId = 53, + Code = "NAV:products.tax-rates", + SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }, + new + { + PermissionId = 56, + Code = "NAV:sales.day-end-report", + SubNavItemId = 46 + }, + new + { + PermissionId = 57, + Code = "NAV:accounts.bank-deposits", + SubNavItemId = 47 + }, + new + { + PermissionId = 58, + Code = "NAV:products.price-adjustments", + SubNavItemId = 48 + }, + new + { + PermissionId = 59, + Code = "NAV:settings.approvals", + SubNavItemId = 49 + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.Property("PoLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PoId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Tax") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("PoLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PoId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("po_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustment", b => + { + b.Property("PriceAdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PriceAdjustmentId")); + + b.Property("AppliedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1By") + .HasColumnType("int"); + + b.Property("ApprovedLevel2At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel2By") + .HasColumnType("int"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("RejectReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("RejectedAt") + .HasColumnType("datetime(6)"); + + b.Property("RejectedBy") + .HasColumnType("int"); + + b.Property("RequiredApprovals") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .HasColumnType("varchar(30)") + .HasDefaultValue("PendingApproval"); + + b.HasKey("PriceAdjustmentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("price_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustmentLine", b => + { + b.Property("PriceAdjustmentLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PriceAdjustmentLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("NewFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PriceAdjustmentId") + .HasColumnType("int"); + + b.HasKey("PriceAdjustmentLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PriceAdjustmentId"); + + b.ToTable("price_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("BatchListsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BrandsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CustomVariantsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("CustomWarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemImagesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("ItemTypesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SellingPricesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SerialNumbersEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("SubcategoriesEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.Property("WarrantyPeriodsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.HasKey("ConfigId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("product_config", null, t => + { + t.HasCheckConstraint("ck_product_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Property("RunId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunId")); + + b.Property("CancelReasonCodeId") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OutputBinId") + .HasColumnType("int"); + + b.Property("ReworkCount") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("ScaleFactor") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("RunId"); + + b.HasIndex("CancelReasonCodeId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("OutputBinId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("TemplateId", "Status"); + + b.ToTable("production_runs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TemplateId")); + + b.Property("Annotations") + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TemplateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("production_templates", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Property("PurchaseInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("VendorInvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("VendorInvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("VendorSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("VendorSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("PurchaseInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.HasIndex("VendorId", "VendorInvoiceNo"); + + b.ToTable("purchase_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.Property("PurchaseInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoiceLineId")); + + b.Property("GrnId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VatTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("PurchaseInvoiceLineId"); + + b.HasIndex("GrnId") + .IsUnique(); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.Property("PurchaseInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PurchaseInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PurchaseInvoiceId") + .HasColumnType("int"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("PurchaseInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("PurchaseInvoiceId"); + + b.ToTable("purchase_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Property("PoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PoId")); + + b.Property("ApprovalRequired") + .HasColumnType("tinyint(1)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("int"); + + b.Property("ApproverUserId") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("PoId"); + + b.HasIndex("ApproverUserId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.HasIndex("Status"); + + b.HasIndex("VendorId"); + + b.ToTable("purchase_orders", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("VendorId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("purchase_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.ToTable("purchase_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b => + { + b.Property("ReasonCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReasonCodeId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Context") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("ReasonCodeId"); + + b.HasIndex("Context", "Code") + .IsUnique(); + + b.ToTable("reason_codes", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Property("RequisitionId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RequisitionId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RequisitionId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.ToTable("requisitions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.Property("ReqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequiredBy") + .HasColumnType("date"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.HasKey("ReqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RequisitionId"); + + b.ToTable("requisition_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Property("RfqId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RequisitionId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RfqId"); + + b.HasIndex("BranchId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("RequisitionId"); + + b.ToTable("rfqs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.Property("RfqLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RfqLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.HasKey("RfqLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RfqId"); + + b.ToTable("rfq_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RoleId")); + + b.Property("AuthRoleId") + .HasColumnType("char(36)") + .HasColumnName("auth_role_id"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSystemRole") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("RoleId"); + + b.HasIndex("AuthRoleId") + .IsUnique(); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("PermissionId") + .HasColumnType("int"); + + b.Property("Actions") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(31); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.Property("RunEdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunEdgeId")); + + b.Property("ChildRunStageId") + .HasColumnType("int"); + + b.Property("ParentRunStageId") + .HasColumnType("int"); + + b.Property("RunId") + .HasColumnType("int"); + + b.HasKey("RunEdgeId"); + + b.HasIndex("ChildRunStageId"); + + b.HasIndex("RunId"); + + b.HasIndex("ParentRunStageId", "ChildRunStageId") + .IsUnique(); + + b.ToTable("run_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Property("RunStageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunStageId")); + + b.Property("ActualEndAt") + .HasColumnType("datetime(6)"); + + b.Property("ActualStartAt") + .HasColumnType("datetime(6)"); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldValues") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TemplateStageId") + .HasColumnType("int"); + + b.HasKey("RunStageId"); + + b.HasIndex("TemplateStageId"); + + b.HasIndex("RunId", "Status"); + + b.ToTable("run_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EventId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("RunId") + .HasColumnType("int"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("EventId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("UserId"); + + b.HasIndex("RunId", "EventId"); + + b.ToTable("run_stage_events", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.Property("RunInputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunInputId")); + + b.Property("ConsumedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ConsumedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DeliveredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("FromRunOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReturnedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnedValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("RunInputId"); + + b.HasIndex("FromRunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.ToTable("run_stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.Property("RunOutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("RunOutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PlannedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ProducedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunStageId") + .HasColumnType("int"); + + b.Property("ScrapReasonCodeId") + .HasColumnType("int"); + + b.Property("ScrappedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TransferredQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("RunOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("RunStageId"); + + b.HasIndex("ScrapReasonCodeId"); + + b.HasIndex("UomId"); + + b.ToTable("run_stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalaryComponent", b => + { + b.Property("SalaryComponentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalaryComponentId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ComponentType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsEpfEtfApplicable") + .HasColumnType("tinyint(1)"); + + b.Property("IsTaxable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SalaryComponentId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_salary_components", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.Property("ConfigId") + .HasColumnType("int"); + + b.Property("AllowCashCustomer") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowDiscounts") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowFreeItems") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("AllowOverStockSale") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("AllowWarehouseSelection") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("CashCustomerId") + .HasColumnType("int"); + + b.Property("DefaultWarehouseId") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ConfigId"); + + b.HasIndex("CashCustomerId"); + + b.HasIndex("DefaultWarehouseId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("sales_config", null, t => + { + t.HasCheckConstraint("ck_sales_config_singleton", "`ConfigId` = 1"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Property("SalesDayEndId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesDayEndId")); + + b.Property("BalanceTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BundleCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("ClosedBy") + .HasColumnType("int"); + + b.Property("CostOfGoodsSold") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipCount") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("SalesDayEndId"); + + b.HasIndex("ClosedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("CashierUserId", "BusinessDate") + .IsUnique(); + + b.ToTable("sales_day_ends", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Property("SalesInvoiceId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatorUserId") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CustomerSnapshotTaxNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("B2C"); + + b.Property("NetPayable") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoundOff") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("InvoiceDate"); + + b.HasIndex("InvoiceNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoices", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Property("SalesInvoiceLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesInvoiceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_invoice_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.Property("SalesInvoiceLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoiceLineSerialId")); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesInvoiceLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesInvoiceLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_invoice_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.Property("SalesInvoicePaymentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesInvoicePaymentId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("GlBankAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("PaymentDate") + .HasColumnType("datetime(6)"); + + b.Property("PaymentMethod") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Cash"); + + b.Property("Reference") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SalesInvoiceId") + .HasColumnType("int"); + + b.HasKey("SalesInvoicePaymentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("SalesInvoiceId"); + + b.ToTable("sales_invoice_payments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.Property("SalesLineAllocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesLineAllocationId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReturned") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesLineAllocationId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId", "SourceLineId"); + + b.ToTable("sales_line_allocations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Property("ReturnId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("ReturnId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_returns", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Property("ReturnLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ReturnLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReturnId") + .HasColumnType("int"); + + b.Property("SalesInvoiceLineId") + .HasColumnType("int"); + + b.HasKey("ReturnLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("ReturnId"); + + b.HasIndex("SalesInvoiceLineId"); + + b.ToTable("sales_return_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.Property("SalesReturnLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesReturnLineSerialId")); + + b.Property("ReturnLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesReturnLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("ReturnLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_return_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Property("SalesSlipId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipId")); + + b.Property("BalanceAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashierUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("CustomerSnapshotName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DayEndId") + .HasColumnType("int"); + + b.Property("DiscountTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("GrandTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PaidAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SlipDate") + .HasColumnType("datetime(6)"); + + b.Property("SlipNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Draft"); + + b.Property("Subtotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipId"); + + b.HasIndex("CashierUserId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("DayEndId"); + + b.HasIndex("SlipDate"); + + b.HasIndex("SlipNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slips", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Property("SalesSlipLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineId")); + + b.Property("BaseCost") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("DiscountAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("DiscountMode") + .HasColumnType("int"); + + b.Property("DiscountPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("FreeQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("IsFreeIssue") + .HasColumnType("tinyint(1)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NetUnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ParentLineId") + .HasColumnType("int"); + + b.Property("PriceSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SalesSlipId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TaxAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("TaxPct") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SalesSlipId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("sales_slip_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.Property("SalesSlipLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SalesSlipLineSerialId")); + + b.Property("SalesSlipLineId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("SalesSlipLineSerialId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SalesSlipLineId", "SerialId") + .IsUnique(); + + b.ToTable("sales_slip_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.Property("SerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialId")); + + b.Property("Barcode") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.Property("WarrantyPeriodMonths") + .HasColumnType("int"); + + b.HasKey("SerialId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.HasIndex("BinId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("ItemId", "SerialNo") + .IsUnique(); + + b.HasIndex("ItemId", "Status"); + + b.HasIndex("WarehouseId", "ItemId", "Status"); + + b.ToTable("serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.Property("SerialMovementId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SerialMovementId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FromWarehouseId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("SerialMovementId"); + + b.HasIndex("SerialId", "SerialMovementId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.ToTable("serial_movements", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.Property("EdgeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EdgeId")); + + b.Property("ChildStageId") + .HasColumnType("int"); + + b.Property("ParentStageId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("EdgeId"); + + b.HasIndex("ChildStageId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("ParentStageId", "ChildStageId") + .IsUnique(); + + b.ToTable("stage_edges", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.Property("InputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("InputId")); + + b.Property("FromOutputId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyUnit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.HasKey("InputId"); + + b.HasIndex("FromOutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.ToTable("stage_inputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.Property("OutputId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OutputId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("QtyPerBatch") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StageId") + .HasColumnType("int"); + + b.Property("UomId") + .HasColumnType("int"); + + b.HasKey("OutputId"); + + b.HasIndex("ItemId"); + + b.HasIndex("StageId"); + + b.HasIndex("UomId"); + + b.ToTable("stage_outputs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Property("AdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + + b.Property("ApprovedLevel1At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1By") + .HasColumnType("int"); + + b.Property("ApprovedLevel2At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel2By") + .HasColumnType("int"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReasonCodeId") + .HasColumnType("int"); + + b.Property("RejectReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("RejectedAt") + .HasColumnType("datetime(6)"); + + b.Property("RejectedBy") + .HasColumnType("int"); + + b.Property("RequiredApprovals") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("AdjustmentId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ReasonCodeId"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.Property("AdjLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjLineId")); + + b.Property("AdjustmentId") + .HasColumnType("int"); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyDelta") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RequestedSerialIds") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("RequestedSerialNos") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.HasKey("AdjLineId"); + + b.HasIndex("AdjustmentId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_adjustment_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Property("CountId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CountType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("CountId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("stock_counts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.Property("CountLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CountLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CountId") + .HasColumnType("int"); + + b.Property("CountedQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SystemQty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Variance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("CountLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("CountId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.ToTable("stock_count_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.Property("LayerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LayerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("GrnLineId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyRemaining") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("ReceiptDate") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LayerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("GrnLineId"); + + b.HasIndex("SerialId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId"); + + b.ToTable("stock_layers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.Property("LedgerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LedgerId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("BinId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Direction") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("varchar(5)"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("QtyBase") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RunningBalance") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("WarehouseId") + .HasColumnType("int"); + + b.HasKey("LedgerId"); + + b.HasIndex("BatchId"); + + b.HasIndex("BinId"); + + b.HasIndex("SerialId"); + + b.HasIndex("UserId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("ItemId", "WarehouseId", "CreatedAt"); + + b.HasIndex("ItemId", "WarehouseId", "LedgerId"); + + b.ToTable("stock_ledger", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Property("TransferId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DestWarehouseId") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SrcWarehouseId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("TransferId"); + + b.HasIndex("BranchId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DestWarehouseId"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("SrcWarehouseId"); + + b.HasIndex("Status"); + + b.ToTable("stock_transfers", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Property("TransferLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransferLineId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("DestBinId") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("QtyReceived") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SrcBinId") + .HasColumnType("int"); + + b.Property("TransferId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("TransferLineId"); + + b.HasIndex("BatchId"); + + b.HasIndex("DestBinId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SrcBinId"); + + b.HasIndex("TransferId"); + + b.ToTable("stock_transfer_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.Property("StockTransferLineSerialId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StockTransferLineSerialId")); + + b.Property("BatchId") + .HasColumnType("int"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("TransferLineId") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("StockTransferLineSerialId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("TransferLineId", "SerialId") + .IsUnique(); + + b.ToTable("stock_transfer_line_serials", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.Property("SubCategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubCategoryId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("SubCategoryId"); + + b.HasIndex("Status"); + + b.HasIndex("CategoryId", "Name") + .IsUnique(); + + b.ToTable("subcategories", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.Property("SubNavItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SubNavItemId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Href") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NavItemId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.HasKey("SubNavItemId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("NavItemId"); + + b.ToTable("sub_nav_items", (string)null); + + b.HasData( + new + { + SubNavItemId = 1, + Code = "products.item", + Href = "/dashboard/products", + Label = "Item", + NavItemId = 2, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 2, + Code = "products.category", + Href = "/dashboard/products/categories", + Label = "Category", + NavItemId = 2, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 3, + Code = "products.brand", + Href = "/dashboard/products/brands", + Label = "Brand", + NavItemId = 2, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 4, + Code = "products.item-type", + Href = "/dashboard/products/item-types", + Label = "Item Type", + NavItemId = 2, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 5, + Code = "products.uom", + Href = "/dashboard/products/uoms", + Label = "UOM", + NavItemId = 2, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 6, + Code = "products.configuration", + Href = "/dashboard/products/settings", + Label = "Configuration", + NavItemId = 2, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 43, + Code = "products.tax-rates", + Href = "/dashboard/products/tax-rates", + Label = "Tax Rates", + NavItemId = 2, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 48, + Code = "products.price-adjustments", + Href = "/dashboard/products/price-adjustments", + Label = "Price Adjustments", + NavItemId = 2, + SortOrder = 8, + Status = "Active" + }, + new + { + SubNavItemId = 29, + Code = "settings.branches", + Href = "/dashboard/settings/branches", + Label = "Branches", + NavItemId = 9, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 7, + Code = "settings.roles", + Href = "/dashboard/settings/roles", + Label = "Roles", + NavItemId = 9, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 8, + Code = "settings.users", + Href = "/dashboard/settings/users", + Label = "Users", + NavItemId = 9, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 24, + Code = "settings.notification-config", + Href = "/dashboard/settings/notification-config", + Label = "Notification Config", + NavItemId = 9, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 49, + Code = "settings.approvals", + Href = "/dashboard/settings/approvals", + Label = "Approvals", + NavItemId = 9, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 40, + Code = "sales.invoices", + Href = "/dashboard/sales/invoices", + Label = "Sales", + NavItemId = 13, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 23, + Code = "sales.bundle-templates", + Href = "/dashboard/sales/bundles/templates", + Label = "Bundle Templates", + NavItemId = 13, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 41, + Code = "sales.free-issues", + Href = "/dashboard/sales/free-issues", + Label = "Free Issues", + NavItemId = 13, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 42, + Code = "sales.day-end", + Href = "/dashboard/sales/day-end", + Label = "Approved Invoices", + NavItemId = 13, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 46, + Code = "sales.day-end-report", + Href = "/dashboard/sales/day-end-report", + Label = "Day-End Report", + NavItemId = 13, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 30, + Code = "procurement.vendors", + Href = "/dashboard/vendors", + Label = "Vendors", + NavItemId = 4, + SortOrder = 0, + Status = "Active" + }, + new + { + SubNavItemId = 17, + Code = "procurement.requisitions", + Href = "/dashboard/procurement/requisitions", + Label = "Requisitions", + NavItemId = 4, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 18, + Code = "procurement.rfqs", + Href = "/dashboard/procurement/rfqs", + Label = "RFQs", + NavItemId = 4, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 19, + Code = "procurement.purchase-orders", + Href = "/dashboard/procurement/purchase-orders", + Label = "Purchase Orders", + NavItemId = 4, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 20, + Code = "procurement.purchase-returns", + Href = "/dashboard/procurement/purchase-returns", + Label = "Purchase Returns", + NavItemId = 4, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 28, + Code = "procurement.purchase-invoices", + Href = "/dashboard/procurement/purchase-invoices", + Label = "Purchase Invoices", + NavItemId = 4, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 9, + Code = "ledgers.trial-balance", + Href = "/dashboard/ledgers/trial-balance", + Label = "Trial Balance", + NavItemId = 11, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 10, + Code = "ledgers.balance-sheet", + Href = "/dashboard/ledgers/balance-sheet", + Label = "Balance Sheet", + NavItemId = 11, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 11, + Code = "ledgers.general-ledger", + Href = "/dashboard/ledgers/general-ledger", + Label = "General Ledger", + NavItemId = 11, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 12, + Code = "ledgers.profit-and-loss", + Href = "/dashboard/ledgers/profit-and-loss", + Label = "Profit & Loss", + NavItemId = 11, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 13, + Code = "ledgers.cash-flow", + Href = "/dashboard/ledgers/cash-flow", + Label = "Cash Flow", + NavItemId = 11, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 14, + Code = "ledgers.budget-vs-actual", + Href = "/dashboard/ledgers/budget-vs-actual", + Label = "Budget vs Actual", + NavItemId = 11, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 16, + Code = "ledgers.tax-report", + Href = "/dashboard/ledgers/tax-report", + Label = "Tax Report", + NavItemId = 11, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 15, + Code = "accounts.bank-accounts", + Href = "/dashboard/accounts/bank-accounts", + Label = "Cash / Bank Accounts", + NavItemId = 12, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 21, + Code = "accounts.cheque-books", + Href = "/dashboard/accounts/cheque-books", + Label = "Cheque Books", + NavItemId = 12, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 22, + Code = "accounts.received-cheques", + Href = "/dashboard/accounts/received-cheques", + Label = "Received Cheques", + NavItemId = 12, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 47, + Code = "accounts.bank-deposits", + Href = "/dashboard/accounts/bank-deposits", + Label = "Bank Deposits", + NavItemId = 12, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 27, + Code = "production.templates", + Href = "/dashboard/production/templates", + Label = "Templates", + NavItemId = 14, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 25, + Code = "production.runs", + Href = "/dashboard/production/runs", + Label = "Runs", + NavItemId = 14, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 26, + Code = "production.produced", + Href = "/dashboard/production/produced", + Label = "Produced", + NavItemId = 14, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 31, + Code = "stock.ledger", + Href = "/dashboard/stock/ledger", + Label = "Stock Ledger", + NavItemId = 6, + SortOrder = 1, + Status = "Active" + }, + new + { + SubNavItemId = 32, + Code = "stock.enquiry", + Href = "/dashboard/stock/enquiry", + Label = "Stock Enquiry", + NavItemId = 6, + SortOrder = 2, + Status = "Active" + }, + new + { + SubNavItemId = 33, + Code = "stock.valuation", + Href = "/dashboard/stock/valuation", + Label = "Valuation", + NavItemId = 6, + SortOrder = 3, + Status = "Active" + }, + new + { + SubNavItemId = 34, + Code = "stock.transfers", + Href = "/dashboard/stock/transfers", + Label = "Transfers", + NavItemId = 6, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 35, + Code = "stock.adjustments", + Href = "/dashboard/stock/adjustments", + Label = "Adjustments", + NavItemId = 6, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 36, + Code = "stock.counts", + Href = "/dashboard/stock/counts", + Label = "Counts", + NavItemId = 6, + SortOrder = 6, + Status = "Active" + }, + new + { + SubNavItemId = 37, + Code = "stock.reorder-alerts", + Href = "/dashboard/stock/reorder-alerts", + Label = "Reorder Alerts", + NavItemId = 6, + SortOrder = 7, + Status = "Active" + }, + new + { + SubNavItemId = 38, + Code = "stock.wastage", + Href = "/dashboard/stock/wastage", + Label = "Wastage", + NavItemId = 6, + SortOrder = 8, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Property("TicketId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TicketId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("TicketId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("Status"); + + b.ToTable("support_tickets", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MessageId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsFromAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SenderUserId") + .HasColumnType("int"); + + b.Property("TicketId") + .HasColumnType("int"); + + b.HasKey("MessageId"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("support_ticket_messages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxRate", b => + { + b.Property("TaxRateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxRateId")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Percentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("TaxRateId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("tax_rates", (string)null); + + b.HasData( + new + { + TaxRateId = 1, + Code = "STANDARD", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Standard", + Percentage = 15m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 2, + Code = "ZERO", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Zero-Rated", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }, + new + { + TaxRateId = 3, + Code = "EXEMPT", + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Name = "Exempt", + Percentage = 0m, + RowVersion = 1u, + Status = "Active" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TaxSlab", b => + { + b.Property("TaxSlabId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TaxSlabId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("LowerBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Rate") + .HasPrecision(6, 4) + .HasColumnType("decimal(6,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("UpperBound") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("TaxSlabId"); + + b.HasIndex("EffectiveFrom"); + + b.ToTable("hr_tax_slabs", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Property("StageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StageId")); + + b.Property("EstimatedMinutes") + .HasColumnType("int"); + + b.Property("FieldDefs") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("PosX") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PosY") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RoleLabel") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("StageId"); + + b.HasIndex("TemplateId"); + + b.ToTable("template_stages", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b => + { + b.Property("UomId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UomId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("UomId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("uoms", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("UserId")); + + b.Property("AuthUserId") + .HasColumnType("char(36)") + .HasColumnName("auth_user_id"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(320) + .HasColumnType("varchar(320)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UserId"); + + b.HasIndex("AuthUserId") + .IsUnique(); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + UserId = 1, + DisplayName = "System", + Status = "Active", + Username = "system" + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.Property("VendorId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("VendorId")); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("varchar(3)") + .HasDefaultValue("LKR"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("TaxReg") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Terms") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("VendorId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("vendors", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Property("QuotationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("RfqId") + .HasColumnType("int"); + + b.Property("VendorId") + .HasColumnType("int"); + + b.HasKey("QuotationId"); + + b.HasIndex("VendorId"); + + b.HasIndex("RfqId", "VendorId") + .IsUnique(); + + b.ToTable("vendor_quotations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.Property("QuotationLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("QuotationLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("LeadDays") + .HasColumnType("int"); + + b.Property("QuotationId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("QuotationLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuotationId"); + + b.ToTable("vendor_quotation_lines", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarehouseId")); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasColumnType("longtext"); + + b.HasKey("WarehouseId"); + + b.HasIndex("BranchId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("warehouses", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyPeriod", b => + { + b.Property("WarrantyPeriodId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyPeriodId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Months") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyPeriodId"); + + b.HasIndex("Months") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("warranty_periods", null, t => + { + t.HasCheckConstraint("ck_warranty_periods_months", "`Months` BETWEEN 1 AND 600"); + }); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.Property("WarrantyRegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WarrantyRegistrationId")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("PeriodMonths") + .HasColumnType("int"); + + b.Property("Qty") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("SerialId") + .HasColumnType("int"); + + b.Property("SourceDocId") + .HasColumnType("int"); + + b.Property("SourceDocType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SourceLineId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("WarrantyRegistrationId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ItemId"); + + b.HasIndex("SerialId"); + + b.HasIndex("SourceDocType", "SourceDocId"); + + b.HasIndex("Status", "EndDate"); + + b.ToTable("warranty_registrations", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WorkShift", b => + { + b.Property("WorkShiftId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("WorkShiftId")); + + b.Property("BreakMinutes") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("GraceMinutes") + .HasColumnType("int"); + + b.Property("IsOvernight") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OtMultiplier") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StandardWorkingMinutes") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Active"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WorkingDaysMask") + .HasColumnType("int"); + + b.HasKey("WorkShiftId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("hr_work_shifts", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => + { + b.HasOne("ERPCore.Domain.Entities.AttendanceUploadBatch", "AttendanceUploadBatch") + .WithMany() + .HasForeignKey("AttendanceUploadBatchId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceUploadBatch"); + + b.Navigation("Employee"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b => + { + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany("Bins") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany() + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("BundleSales") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSale", "BundleSale") + .WithMany("Lines") + .HasForeignKey("BundleSaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSale"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b => + { + b.HasOne("ERPCore.Domain.Entities.BundleSaleTemplate", "BundleSaleTemplate") + .WithMany("Lines") + .HasForeignKey("BundleSaleTemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BundleSaleTemplate"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("DefaultWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Department", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Employee", "HeadEmployee") + .WithMany() + .HasForeignKey("HeadEmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Department", "ParentDepartment") + .WithMany() + .HasForeignKey("ParentDepartmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("HeadEmployee"); + + b.Navigation("ParentDepartment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.DocumentApproval", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Decider") + .WithMany() + .HasForeignKey("DecidedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Decider"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Designation", "Designation") + .WithMany() + .HasForeignKey("DesignationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.EmploymentType", "EmploymentType") + .WithMany() + .HasForeignKey("EmploymentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Employee", "ReportingManager") + .WithMany() + .HasForeignKey("ReportingManagerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.WorkShift", "WorkShift") + .WithMany() + .HasForeignKey("WorkShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Department"); + + b.Navigation("Designation"); + + b.Navigation("EmploymentType"); + + b.Navigation("ReportingManager"); + + b.Navigation("User"); + + b.Navigation("WorkShift"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeBankDetail", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.HrDocumentType", "HrDocumentType") + .WithMany() + .HasForeignKey("HrDocumentTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("HrDocumentType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructureLine", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeSalaryStructure", "EmployeeSalaryStructure") + .WithMany("Lines") + .HasForeignKey("EmployeeSalaryStructureId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EmployeeSalaryStructure"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany() + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ExpenseTitle", "Title") + .WithMany() + .HasForeignKey("ExpenseTitleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + + b.Navigation("Category"); + + b.Navigation("Creator"); + + b.Navigation("Title"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpensePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Expense", "Expense") + .WithMany("Payments") + .HasForeignKey("ExpenseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Expense"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseTitle", b => + { + b.HasOne("ERPCore.Domain.Entities.ExpenseCategory", "Category") + .WithMany("Titles") + .HasForeignKey("ExpenseCategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany() + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany("Lines") + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PoLine", "PoLine") + .WithMany() + .HasForeignKey("PoLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Bin"); + + b.Navigation("Grn"); + + b.Navigation("Item"); + + b.Navigation("PoLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("Serials") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GrnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLineWarrantyNumber", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany("WarrantyNumbers") + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom") + .WithMany() + .HasForeignKey("BaseUomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Brand", "Brand") + .WithMany() + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor") + .WithMany() + .HasForeignKey("DefaultVendorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("SubCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TaxRate", "TaxRate") + .WithMany() + .HasForeignKey("TaxRateId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("DefaultVendor"); + + b.Navigation("SubCategory"); + + b.Navigation("TaxRate"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemImage", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemPriceHistory", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PriceAdjustment", "PriceAdjustment") + .WithMany() + .HasForeignKey("PriceAdjustmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("PriceAdjustment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany("ReorderSettings") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveBalance", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LeaveRequest", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoanInstallment", b => + { + b.HasOne("ERPCore.Domain.Entities.EmployeeLoan", "EmployeeLoan") + .WithMany("Installments") + .HasForeignKey("EmployeeLoanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany() + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EmployeeLoan"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.LoginActivity", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfigModule", b => + { + b.HasOne("ERPCore.Domain.Entities.NotificationConfig", "NotificationConfig") + .WithMany("Modules") + .HasForeignKey("NotificationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationConfig"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationRecipient", b => + { + b.HasOne("ERPCore.Domain.Entities.Notification", "Notification") + .WithMany("Recipients") + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PayrollRun", "PayrollRun") + .WithMany("Lines") + .HasForeignKey("PayrollRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollRun"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLineComponent", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithMany("Components") + .HasForeignKey("PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalaryComponent", "SalaryComponent") + .WithMany() + .HasForeignKey("SalaryComponentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("PayrollLine"); + + b.Navigation("SalaryComponent"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Payslip", b => + { + b.HasOne("ERPCore.Domain.Entities.PayrollLine", "PayrollLine") + .WithOne() + .HasForeignKey("ERPCore.Domain.Entities.Payslip", "PayrollLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany() + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ERPCore.Domain.Entities.SubNavItem", "SubNavItem") + .WithMany() + .HasForeignKey("SubNavItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("NavItem"); + + b.Navigation("SubNavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PriceAdjustment", "PriceAdjustment") + .WithMany("Lines") + .HasForeignKey("PriceAdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PriceAdjustment"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "CancelReason") + .WithMany() + .HasForeignKey("CancelReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Bin", "OutputBin") + .WithMany() + .HasForeignKey("OutputBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Runs") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CancelReason"); + + b.Navigation("Creator"); + + b.Navigation("OutputBin"); + + b.Navigation("Template"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Grn", "Grn") + .WithMany() + .HasForeignKey("GrnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Lines") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grn"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseInvoice", "PurchaseInvoice") + .WithMany("Payments") + .HasForeignKey("PurchaseInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("PurchaseInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApproverUserId"); + + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Requisition"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Vendor"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PurchaseReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Return"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Requester") + .WithMany() + .HasForeignKey("RequestedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requester"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany("Lines") + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition") + .WithMany() + .HasForeignKey("RequisitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Requisition"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Lines") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Rfq"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b => + { + b.HasOne("ERPCore.Domain.Entities.Permission", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStage", "ChildRunStage") + .WithMany() + .HasForeignKey("ChildRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "ParentRunStage") + .WithMany() + .HasForeignKey("ParentRunStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Edges") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRunStage"); + + b.Navigation("ParentRunStage"); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Stages") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "TemplateStage") + .WithMany() + .HasForeignKey("TemplateStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Run"); + + b.Navigation("TemplateStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageEvent", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Events") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Run"); + + b.Navigation("RunStage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.RunStageOutput", "FromRunOutput") + .WithMany() + .HasForeignKey("FromRunOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Inputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromRunOutput"); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.RunStage", "RunStage") + .WithMany("Outputs") + .HasForeignKey("RunStageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ScrapReason") + .WithMany() + .HasForeignKey("ScrapReasonCodeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("RunStage"); + + b.Navigation("ScrapReason"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesConfig", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "CashCustomer") + .WithMany() + .HasForeignKey("CashCustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DefaultWarehouse") + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CashCustomer"); + + b.Navigation("DefaultWarehouse"); + + b.Navigation("UpdatedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "ClosedByUser") + .WithMany() + .HasForeignKey("ClosedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("ClosedByUser"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Lines") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesInvoice"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany("Serials") + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesInvoiceLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoicePayment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoice", "SalesInvoice") + .WithMany("Payments") + .HasForeignKey("SalesInvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("SalesInvoice"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesLineAllocation", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Customer"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesReturn", "Return") + .WithMany("Lines") + .HasForeignKey("ReturnId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesInvoiceLine", "SalesInvoiceLine") + .WithMany() + .HasForeignKey("SalesInvoiceLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("Return"); + + b.Navigation("SalesInvoiceLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesReturnLine", "ReturnLine") + .WithMany("Serials") + .HasForeignKey("ReturnLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ReturnLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "CashierUser") + .WithMany() + .HasForeignKey("CashierUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesDayEnd", "DayEnd") + .WithMany("Slips") + .HasForeignKey("DayEndId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CashierUser"); + + b.Navigation("Customer"); + + b.Navigation("DayEnd"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.SalesSlip", "SalesSlip") + .WithMany("Lines") + .HasForeignKey("SalesSlipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("Item"); + + b.Navigation("SalesSlip"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.SalesSlipLine", "SalesSlipLine") + .WithMany("Serials") + .HasForeignKey("SalesSlipLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesSlipLine"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b => + { + b.HasOne("ERPCore.Domain.Entities.Bin", "Bin") + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Bin"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SerialMovement", b => + { + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageEdge", b => + { + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ChildStage") + .WithMany() + .HasForeignKey("ChildStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "ParentStage") + .WithMany() + .HasForeignKey("ParentStageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Edges") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildStage"); + + b.Navigation("ParentStage"); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageInput", b => + { + b.HasOne("ERPCore.Domain.Entities.StageOutput", "FromOutput") + .WithMany() + .HasForeignKey("FromOutputId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Inputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromOutput"); + + b.Navigation("Item"); + + b.Navigation("Stage"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StageOutput", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.TemplateStage", "Stage") + .WithMany("Outputs") + .HasForeignKey("StageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Uom", "Uom") + .WithMany() + .HasForeignKey("UomId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("Stage"); + + b.Navigation("Uom"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.ReasonCode", "ReasonCode") + .WithMany() + .HasForeignKey("ReasonCodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("ReasonCode"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.StockAdjustment", "Adjustment") + .WithMany("Lines") + .HasForeignKey("AdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Adjustment"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockCount", "Count") + .WithMany("Lines") + .HasForeignKey("CountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Batch"); + + b.Navigation("Count"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.GrnLine", "GrnLine") + .WithMany() + .HasForeignKey("GrnLineId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Batch"); + + b.Navigation("GrnLine"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("BinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", null) + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", null) + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "DestWarehouse") + .WithMany() + .HasForeignKey("DestWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Warehouse", "SrcWarehouse") + .WithMany() + .HasForeignKey("SrcWarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + + b.Navigation("DestWarehouse"); + + b.Navigation("SrcWarehouse"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("DestBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", null) + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Bin", null) + .WithMany() + .HasForeignKey("SrcBinId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.StockTransfer", "Transfer") + .WithMany("Lines") + .HasForeignKey("TransferId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Transfer"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLineSerial", b => + { + b.HasOne("ERPCore.Domain.Entities.Batch", null) + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.StockTransferLine", "TransferLine") + .WithMany("Serials") + .HasForeignKey("TransferLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Serial"); + + b.Navigation("TransferLine"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubCategory", b => + { + b.HasOne("ERPCore.Domain.Entities.Category", "Category") + .WithMany("SubCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SubNavItem", b => + { + b.HasOne("ERPCore.Domain.Entities.NavItem", "NavItem") + .WithMany("Children") + .HasForeignKey("NavItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavItem"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicketMessage", b => + { + b.HasOne("ERPCore.Domain.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.HasOne("ERPCore.Domain.Entities.ProductionTemplate", "Template") + .WithMany("Stages") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.User", b => + { + b.HasOne("ERPCore.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq") + .WithMany("Quotations") + .HasForeignKey("RfqId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor") + .WithMany() + .HasForeignKey("VendorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Rfq"); + + b.Navigation("Vendor"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation") + .WithMany("Lines") + .HasForeignKey("QuotationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Quotation"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.WarrantyRegistration", b => + { + b.HasOne("ERPCore.Domain.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.Serial", "Serial") + .WithMany() + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Customer"); + + b.Navigation("Item"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeLoan", b => + { + b.Navigation("Installments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.EmployeeSalaryStructure", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b => + { + b.Navigation("Serials"); + + b.Navigation("WarrantyNumbers"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Item", b => + { + b.Navigation("ReorderSettings"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Notification", b => + { + b.Navigation("Recipients"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.NotificationConfig", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollLine", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PayrollRun", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => + { + b.Navigation("Edges"); + + b.Navigation("Events"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionTemplate", b => + { + b.Navigation("Edges"); + + b.Navigation("Runs"); + + b.Navigation("Stages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b => + { + b.Navigation("Lines"); + + b.Navigation("Quotations"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.RunStage", b => + { + b.Navigation("Events"); + + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesDayEnd", b => + { + b.Navigation("BundleSales"); + + b.Navigation("Slips"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoice", b => + { + b.Navigation("Lines"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesInvoiceLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturn", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesReturnLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlip", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SalesSlipLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransfer", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.StockTransferLine", b => + { + b.Navigation("Serials"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.TemplateStage", b => + { + b.Navigation("Inputs"); + + b.Navigation("Outputs"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b => + { + b.Navigation("Bins"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Backend/ERPCore/Migrations/20261009070055_AddDualApproval.cs b/Backend/ERPCore/Migrations/20261009070055_AddDualApproval.cs new file mode 100644 index 0000000..6f8b0e9 --- /dev/null +++ b/Backend/ERPCore/Migrations/20261009070055_AddDualApproval.cs @@ -0,0 +1,473 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace ERPCore.Migrations +{ + /// + public partial class AddDualApproval : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Status", + table: "stock_adjustments", + type: "varchar(30)", + maxLength: 30, + nullable: false, + collation: "utf8mb4_unicode_ci", + oldClrType: typeof(string), + oldType: "varchar(20)", + oldMaxLength: 20) + .OldAnnotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.AddColumn( + name: "ApprovedLevel1At", + table: "stock_adjustments", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "ApprovedLevel1By", + table: "stock_adjustments", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "ApprovedLevel2At", + table: "stock_adjustments", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "ApprovedLevel2By", + table: "stock_adjustments", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "CancelledAt", + table: "stock_adjustments", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "PostedAt", + table: "stock_adjustments", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "RejectReason", + table: "stock_adjustments", + type: "varchar(250)", + maxLength: 250, + nullable: true, + collation: "utf8mb4_unicode_ci"); + + migrationBuilder.AddColumn( + name: "RejectedAt", + table: "stock_adjustments", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "RejectedBy", + table: "stock_adjustments", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "RequiredApprovals", + table: "stock_adjustments", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "RequestedSerialIds", + table: "stock_adjustment_lines", + type: "varchar(4000)", + maxLength: 4000, + nullable: true, + collation: "utf8mb4_unicode_ci"); + + migrationBuilder.AddColumn( + name: "RequestedSerialNos", + table: "stock_adjustment_lines", + type: "varchar(4000)", + maxLength: 4000, + nullable: true, + collation: "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "approval_config", + columns: table => new + { + ApprovalConfigId = table.Column(type: "int", nullable: false), + StockAdjustmentLevels = table.Column(type: "int", nullable: false), + StockAdjustmentLevel2ThresholdValue = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false), + PriceAdjustmentLevels = table.Column(type: "int", nullable: false), + PriceAdjustmentLevel2ThresholdPercent = table.Column(type: "decimal(9,4)", precision: 9, scale: 4, nullable: false), + Level2RoleCodes = table.Column(type: "varchar(500)", maxLength: 500, nullable: true, collation: "utf8mb4_unicode_ci"), + UpdatedAt = table.Column(type: "datetime(6)", nullable: true), + UpdatedBy = table.Column(type: "int", nullable: true), + RowVersion = table.Column(type: "int unsigned", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_approval_config", x => x.ApprovalConfigId); + table.CheckConstraint("ck_approval_config_singleton", "`ApprovalConfigId` = 1"); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "document_approvals", + columns: table => new + { + DocumentApprovalId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + DocType = table.Column(type: "varchar(10)", maxLength: 10, nullable: false, collation: "utf8mb4_unicode_ci"), + DocId = table.Column(type: "int", nullable: false), + Level = table.Column(type: "int", nullable: false), + Decision = table.Column(type: "varchar(20)", maxLength: 20, nullable: false, collation: "utf8mb4_unicode_ci"), + DecidedBy = table.Column(type: "int", nullable: false), + DecidedAt = table.Column(type: "datetime(6)", nullable: false), + Comment = table.Column(type: "varchar(250)", maxLength: 250, nullable: true, collation: "utf8mb4_unicode_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_document_approvals", x => x.DocumentApprovalId); + table.ForeignKey( + name: "FK_document_approvals_users_DecidedBy", + column: x => x.DecidedBy, + principalTable: "users", + principalColumn: "UserId", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "price_adjustments", + columns: table => new + { + PriceAdjustmentId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + DocNo = table.Column(type: "varchar(30)", maxLength: 30, nullable: false, collation: "utf8mb4_unicode_ci"), + Reason = table.Column(type: "varchar(250)", maxLength: 250, nullable: false, collation: "utf8mb4_unicode_ci"), + Status = table.Column(type: "varchar(30)", maxLength: 30, nullable: false, defaultValue: "PendingApproval", collation: "utf8mb4_unicode_ci"), + RequiredApprovals = table.Column(type: "int", nullable: false), + CreatedBy = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + ApprovedLevel1By = table.Column(type: "int", nullable: true), + ApprovedLevel1At = table.Column(type: "datetime(6)", nullable: true), + ApprovedLevel2By = table.Column(type: "int", nullable: true), + ApprovedLevel2At = table.Column(type: "datetime(6)", nullable: true), + RejectedBy = table.Column(type: "int", nullable: true), + RejectedAt = table.Column(type: "datetime(6)", nullable: true), + RejectReason = table.Column(type: "varchar(250)", maxLength: 250, nullable: true, collation: "utf8mb4_unicode_ci"), + CancelledAt = table.Column(type: "datetime(6)", nullable: true), + AppliedAt = table.Column(type: "datetime(6)", nullable: true), + RowVersion = table.Column(type: "int unsigned", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_price_adjustments", x => x.PriceAdjustmentId); + table.ForeignKey( + name: "FK_price_adjustments_users_CreatedBy", + column: x => x.CreatedBy, + principalTable: "users", + principalColumn: "UserId", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "item_price_history", + columns: table => new + { + ItemPriceHistoryId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ItemId = table.Column(type: "int", nullable: false), + PriceAdjustmentId = table.Column(type: "int", nullable: true), + OldMinPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + OldMaxPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + OldFlatPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + NewMinPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + NewMaxPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + NewFlatPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + RequestedBy = table.Column(type: "int", nullable: false), + ApprovedLevel1By = table.Column(type: "int", nullable: true), + ApprovedLevel2By = table.Column(type: "int", nullable: true), + AppliedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_item_price_history", x => x.ItemPriceHistoryId); + table.ForeignKey( + name: "FK_item_price_history_items_ItemId", + column: x => x.ItemId, + principalTable: "items", + principalColumn: "ItemId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_item_price_history_price_adjustments_PriceAdjustmentId", + column: x => x.PriceAdjustmentId, + principalTable: "price_adjustments", + principalColumn: "PriceAdjustmentId", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.CreateTable( + name: "price_adjustment_lines", + columns: table => new + { + PriceAdjustmentLineId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + PriceAdjustmentId = table.Column(type: "int", nullable: false), + ItemId = table.Column(type: "int", nullable: false), + OldMinPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + OldMaxPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + OldFlatPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + NewMinPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + NewMaxPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + NewFlatPrice = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_price_adjustment_lines", x => x.PriceAdjustmentLineId); + table.ForeignKey( + name: "FK_price_adjustment_lines_items_ItemId", + column: x => x.ItemId, + principalTable: "items", + principalColumn: "ItemId", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_price_adjustment_lines_price_adjustments_PriceAdjustmentId", + column: x => x.PriceAdjustmentId, + principalTable: "price_adjustments", + principalColumn: "PriceAdjustmentId", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("Relational:Collation", "utf8mb4_unicode_ci"); + + migrationBuilder.InsertData( + table: "approval_config", + columns: new[] { "ApprovalConfigId", "Level2RoleCodes", "PriceAdjustmentLevel2ThresholdPercent", "PriceAdjustmentLevels", "RowVersion", "StockAdjustmentLevel2ThresholdValue", "StockAdjustmentLevels", "UpdatedAt", "UpdatedBy" }, + values: new object[] { 1, null, 0m, 2, 1u, 0m, 2, null, null }); + + migrationBuilder.InsertData( + table: "sub_nav_items", + columns: new[] { "SubNavItemId", "Code", "Href", "Icon", "Label", "NavItemId", "SortOrder" }, + values: new object[,] + { + { 48, "products.price-adjustments", "/dashboard/products/price-adjustments", null, "Price Adjustments", 2, 8 }, + { 49, "settings.approvals", "/dashboard/settings/approvals", null, "Approvals", 9, 4 } + }); + + migrationBuilder.InsertData( + table: "permissions", + columns: new[] { "PermissionId", "Code", "NavItemId", "SubNavItemId" }, + values: new object[,] + { + { 58, "NAV:products.price-adjustments", null, 48 }, + { 59, "NAV:settings.approvals", null, 49 } + }); + + migrationBuilder.CreateIndex( + name: "IX_stock_adjustments_Status", + table: "stock_adjustments", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_document_approvals_DecidedBy", + table: "document_approvals", + column: "DecidedBy"); + + migrationBuilder.CreateIndex( + name: "IX_document_approvals_DocType_DocId", + table: "document_approvals", + columns: new[] { "DocType", "DocId" }); + + migrationBuilder.CreateIndex( + name: "IX_item_price_history_ItemId_AppliedAt", + table: "item_price_history", + columns: new[] { "ItemId", "AppliedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_item_price_history_PriceAdjustmentId", + table: "item_price_history", + column: "PriceAdjustmentId"); + + migrationBuilder.CreateIndex( + name: "IX_price_adjustment_lines_ItemId", + table: "price_adjustment_lines", + column: "ItemId"); + + migrationBuilder.CreateIndex( + name: "IX_price_adjustment_lines_PriceAdjustmentId", + table: "price_adjustment_lines", + column: "PriceAdjustmentId"); + + migrationBuilder.CreateIndex( + name: "IX_price_adjustments_CreatedBy", + table: "price_adjustments", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_price_adjustments_DocNo", + table: "price_adjustments", + column: "DocNo", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_price_adjustments_Status", + table: "price_adjustments", + column: "Status"); + + // Existing adjustments all auto-posted, so they need no approval rows — RequiredApprovals = 0 + // (the column default) marks them as "posted without approval". + + // Price Adjustments (58): Admin and every role that already reaches Products (NAV:products 2, + // products.item 11). Approval settings (59): Admin only. Actions 31 = all, including Approve — + // admins should narrow Approve to the people who really approve (at least two per branch). + migrationBuilder.Sql(""" + INSERT IGNORE INTO `role_permissions` (`RoleId`, `PermissionId`, `Actions`) + SELECT r.`RoleId`, 58, 31 + FROM `roles` r + WHERE LOWER(r.`Code`) = 'admin' + OR EXISTS (SELECT 1 FROM `role_permissions` rp + WHERE rp.`RoleId` = r.`RoleId` AND rp.`PermissionId` IN (2, 11)); + """); + migrationBuilder.Sql(""" + INSERT IGNORE INTO `role_permissions` (`RoleId`, `PermissionId`, `Actions`) + SELECT r.`RoleId`, 59, 31 FROM `roles` r WHERE LOWER(r.`Code`) = 'admin'; + """); + + // The approval notifications are on in the bell by default — without them nobody learns a + // request is waiting. Same guarded insert as EnableLowStockAlerts. + foreach (var key in new[] { "approval.pending", "approval.approved", "approval.rejected" }) + { + migrationBuilder.Sql($""" + INSERT INTO `notification_config_modules` (`NotificationConfigId`, `ModuleKey`, `EmailEnabled`, `SmsEnabled`, `InAppEnabled`) + SELECT c.`NotificationConfigId`, '{key}', 0, 0, 1 + FROM `notification_config` c + WHERE c.`NotificationConfigId` = 1 + AND NOT EXISTS (SELECT 1 FROM `notification_config_modules` m WHERE m.`ModuleKey` = '{key}'); + """); + } + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM `role_permissions` WHERE `PermissionId` IN (58, 59);"); + migrationBuilder.Sql("DELETE FROM `notification_config_modules` WHERE `ModuleKey` IN ('approval.pending', 'approval.approved', 'approval.rejected');"); + + migrationBuilder.DropTable( + name: "approval_config"); + + migrationBuilder.DropTable( + name: "document_approvals"); + + migrationBuilder.DropTable( + name: "item_price_history"); + + migrationBuilder.DropTable( + name: "price_adjustment_lines"); + + migrationBuilder.DropTable( + name: "price_adjustments"); + + migrationBuilder.DropIndex( + name: "IX_stock_adjustments_Status", + table: "stock_adjustments"); + + migrationBuilder.DeleteData( + table: "permissions", + keyColumn: "PermissionId", + keyValue: 58); + + migrationBuilder.DeleteData( + table: "permissions", + keyColumn: "PermissionId", + keyValue: 59); + + migrationBuilder.DeleteData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 48); + + migrationBuilder.DeleteData( + table: "sub_nav_items", + keyColumn: "SubNavItemId", + keyValue: 49); + + migrationBuilder.DropColumn( + name: "ApprovedLevel1At", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "ApprovedLevel1By", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "ApprovedLevel2At", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "ApprovedLevel2By", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "CancelledAt", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "PostedAt", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "RejectReason", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "RejectedAt", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "RejectedBy", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "RequiredApprovals", + table: "stock_adjustments"); + + migrationBuilder.DropColumn( + name: "RequestedSerialIds", + table: "stock_adjustment_lines"); + + migrationBuilder.DropColumn( + name: "RequestedSerialNos", + table: "stock_adjustment_lines"); + + migrationBuilder.AlterColumn( + name: "Status", + table: "stock_adjustments", + type: "varchar(20)", + maxLength: 20, + nullable: false, + collation: "utf8mb4_unicode_ci", + oldClrType: typeof(string), + oldType: "varchar(30)", + oldMaxLength: 30) + .OldAnnotation("Relational:Collation", "utf8mb4_unicode_ci"); + } + } +} diff --git a/Backend/ERPCore/Migrations/ErpDbContextModelSnapshot.cs b/Backend/ERPCore/Migrations/ErpDbContextModelSnapshot.cs index c32dfc2..e09569e 100644 --- a/Backend/ERPCore/Migrations/ErpDbContextModelSnapshot.cs +++ b/Backend/ERPCore/Migrations/ErpDbContextModelSnapshot.cs @@ -23,6 +23,58 @@ namespace ERPCore.Migrations MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + modelBuilder.Entity("ERPCore.Domain.Entities.ApprovalConfig", b => + { + b.Property("ApprovalConfigId") + .HasColumnType("int"); + + b.Property("Level2RoleCodes") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PriceAdjustmentLevel2ThresholdPercent") + .HasPrecision(9, 4) + .HasColumnType("decimal(9,4)"); + + b.Property("PriceAdjustmentLevels") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("StockAdjustmentLevel2ThresholdValue") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("StockAdjustmentLevels") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("int"); + + b.HasKey("ApprovalConfigId"); + + b.ToTable("approval_config", null, t => + { + t.HasCheckConstraint("ck_approval_config_singleton", "`ApprovalConfigId` = 1"); + }); + + b.HasData( + new + { + ApprovalConfigId = 1, + PriceAdjustmentLevel2ThresholdPercent = 0m, + PriceAdjustmentLevels = 2, + RowVersion = 1u, + StockAdjustmentLevel2ThresholdValue = 0m, + StockAdjustmentLevels = 2 + }); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b => { b.Property("AttendanceRecordId") @@ -214,6 +266,133 @@ namespace ERPCore.Migrations b.ToTable("audit_logs", (string)null); }); + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.Property("BankDepositId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BankDepositId")); + + b.Property("Amount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("BranchId") + .HasColumnType("int"); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CancelReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DepositDate") + .HasColumnType("date"); + + b.Property("DepositSlipNo") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("DepositedBy") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("FromCashAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("FromCashAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("FromGlCashAccountId") + .HasColumnType("bigint"); + + b.Property("GlJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("GlPostedAt") + .HasColumnType("datetime(6)"); + + b.Property("GlReversalJournalNo") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Posted"); + + b.Property("ToAccountNumber") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ToBankAccountCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ToBankAccountName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ToBankName") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("ToGlBankAccountId") + .HasColumnType("bigint"); + + b.HasKey("BankDepositId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("ToGlBankAccountId"); + + b.HasIndex("BranchId", "BusinessDate", "Status"); + + b.ToTable("bank_deposits", null, t => + { + t.HasCheckConstraint("ck_bank_deposits_amount", "`Amount` > 0"); + }); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => { b.Property("BatchId") @@ -435,6 +614,14 @@ namespace ERPCore.Migrations b.Property("BundleSaleTemplateId") .HasColumnType("int"); + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + b.Property("CashierUserId") .HasColumnType("int"); @@ -904,6 +1091,49 @@ namespace ERPCore.Migrations b.ToTable("hr_designations", (string)null); }); + modelBuilder.Entity("ERPCore.Domain.Entities.DocumentApproval", b => + { + b.Property("DocumentApprovalId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DocumentApprovalId")); + + b.Property("Comment") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("DecidedAt") + .HasColumnType("datetime(6)"); + + b.Property("DecidedBy") + .HasColumnType("int"); + + b.Property("Decision") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DocId") + .HasColumnType("int"); + + b.Property("DocType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Level") + .HasColumnType("int"); + + b.HasKey("DocumentApprovalId"); + + b.HasIndex("DecidedBy"); + + b.HasIndex("DocType", "DocId"); + + b.ToTable("document_approvals", (string)null); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => { b.Property("EmployeeId") @@ -1989,6 +2219,65 @@ namespace ERPCore.Migrations b.ToTable("item_images", (string)null); }); + modelBuilder.Entity("ERPCore.Domain.Entities.ItemPriceHistory", b => + { + b.Property("ItemPriceHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ItemPriceHistoryId")); + + b.Property("AppliedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1By") + .HasColumnType("int"); + + b.Property("ApprovedLevel2By") + .HasColumnType("int"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("NewFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PriceAdjustmentId") + .HasColumnType("int"); + + b.Property("RequestedBy") + .HasColumnType("int"); + + b.HasKey("ItemPriceHistoryId"); + + b.HasIndex("PriceAdjustmentId"); + + b.HasIndex("ItemId", "AppliedAt"); + + b.ToTable("item_price_history", (string)null); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => { b.Property("ReorderId") @@ -3343,6 +3632,42 @@ namespace ERPCore.Migrations PermissionId = 53, Code = "NAV:products.tax-rates", SubNavItemId = 43 + }, + new + { + PermissionId = 54, + Code = "NAV:accounts.expenses", + SubNavItemId = 44 + }, + new + { + PermissionId = 55, + Code = "NAV:accounts.expense-setup", + SubNavItemId = 45 + }, + new + { + PermissionId = 56, + Code = "NAV:sales.day-end-report", + SubNavItemId = 46 + }, + new + { + PermissionId = 57, + Code = "NAV:accounts.bank-deposits", + SubNavItemId = 47 + }, + new + { + PermissionId = 58, + Code = "NAV:products.price-adjustments", + SubNavItemId = 48 + }, + new + { + PermissionId = 59, + Code = "NAV:settings.approvals", + SubNavItemId = 49 }); }); @@ -3390,6 +3715,131 @@ namespace ERPCore.Migrations b.ToTable("po_lines", (string)null); }); + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustment", b => + { + b.Property("PriceAdjustmentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PriceAdjustmentId")); + + b.Property("AppliedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1By") + .HasColumnType("int"); + + b.Property("ApprovedLevel2At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel2By") + .HasColumnType("int"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("DocNo") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("RejectReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("RejectedAt") + .HasColumnType("datetime(6)"); + + b.Property("RejectedBy") + .HasColumnType("int"); + + b.Property("RequiredApprovals") + .HasColumnType("int"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("int unsigned"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .HasColumnType("varchar(30)") + .HasDefaultValue("PendingApproval"); + + b.HasKey("PriceAdjustmentId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("DocNo") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("price_adjustments", (string)null); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustmentLine", b => + { + b.Property("PriceAdjustmentLineId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("PriceAdjustmentLineId")); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("NewFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldFlatPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMaxPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMinPrice") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("PriceAdjustmentId") + .HasColumnType("int"); + + b.HasKey("PriceAdjustmentLineId"); + + b.HasIndex("ItemId"); + + b.HasIndex("PriceAdjustmentId"); + + b.ToTable("price_adjustment_lines", (string)null); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => { b.Property("ConfigId") @@ -4910,6 +5360,13 @@ namespace ERPCore.Migrations b.Property("PaymentDate") .HasColumnType("datetime(6)"); + b.Property("PaymentMethod") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("Cash"); + b.Property("Reference") .HasMaxLength(100) .HasColumnType("varchar(100)"); @@ -5112,6 +5569,14 @@ namespace ERPCore.Migrations .HasPrecision(18, 4) .HasColumnType("decimal(18,4)"); + b.Property("CardAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("CashAmount") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + b.Property("CashierUserId") .HasColumnType("int"); @@ -5549,9 +6014,24 @@ namespace ERPCore.Migrations MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AdjustmentId")); + b.Property("ApprovedLevel1At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel1By") + .HasColumnType("int"); + + b.Property("ApprovedLevel2At") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedLevel2By") + .HasColumnType("int"); + b.Property("BranchId") .HasColumnType("int"); + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + b.Property("CreatedAt") .HasColumnType("datetime(6)"); @@ -5570,17 +6050,33 @@ namespace ERPCore.Migrations b.Property("GlPostedAt") .HasColumnType("datetime(6)"); + b.Property("PostedAt") + .HasColumnType("datetime(6)"); + b.Property("ReasonCodeId") .HasColumnType("int"); + b.Property("RejectReason") + .HasMaxLength(250) + .HasColumnType("varchar(250)"); + + b.Property("RejectedAt") + .HasColumnType("datetime(6)"); + + b.Property("RejectedBy") + .HasColumnType("int"); + + b.Property("RequiredApprovals") + .HasColumnType("int"); + b.Property("RowVersion") .IsConcurrencyToken() .HasColumnType("int unsigned"); b.Property("Status") .IsRequired() - .HasMaxLength(20) - .HasColumnType("varchar(20)"); + .HasMaxLength(30) + .HasColumnType("varchar(30)"); b.Property("WarehouseId") .HasColumnType("int"); @@ -5596,6 +6092,8 @@ namespace ERPCore.Migrations b.HasIndex("ReasonCodeId"); + b.HasIndex("Status"); + b.HasIndex("WarehouseId"); b.ToTable("stock_adjustments", (string)null); @@ -5625,6 +6123,14 @@ namespace ERPCore.Migrations .HasPrecision(18, 4) .HasColumnType("decimal(18,4)"); + b.Property("RequestedSerialIds") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("RequestedSerialNos") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + b.Property("SerialId") .HasColumnType("int"); @@ -6189,6 +6695,16 @@ namespace ERPCore.Migrations Status = "Active" }, new + { + SubNavItemId = 48, + Code = "products.price-adjustments", + Href = "/dashboard/products/price-adjustments", + Label = "Price Adjustments", + NavItemId = 2, + SortOrder = 8, + Status = "Active" + }, + new { SubNavItemId = 29, Code = "settings.branches", @@ -6229,6 +6745,16 @@ namespace ERPCore.Migrations Status = "Active" }, new + { + SubNavItemId = 49, + Code = "settings.approvals", + Href = "/dashboard/settings/approvals", + Label = "Approvals", + NavItemId = 9, + SortOrder = 4, + Status = "Active" + }, + new { SubNavItemId = 40, Code = "sales.invoices", @@ -6263,12 +6789,22 @@ namespace ERPCore.Migrations SubNavItemId = 42, Code = "sales.day-end", Href = "/dashboard/sales/day-end", - Label = "Day End", + Label = "Approved Invoices", NavItemId = 13, SortOrder = 4, Status = "Active" }, new + { + SubNavItemId = 46, + Code = "sales.day-end-report", + Href = "/dashboard/sales/day-end-report", + Label = "Day-End Report", + NavItemId = 13, + SortOrder = 5, + Status = "Active" + }, + new { SubNavItemId = 30, Code = "procurement.vendors", @@ -6429,6 +6965,36 @@ namespace ERPCore.Migrations Status = "Active" }, new + { + SubNavItemId = 44, + Code = "accounts.expenses", + Href = "/dashboard/accounts/expenses", + Label = "Expenses", + NavItemId = 12, + SortOrder = 4, + Status = "Active" + }, + new + { + SubNavItemId = 45, + Code = "accounts.expense-setup", + Href = "/dashboard/accounts/expense-setup", + Label = "Expense Setup", + NavItemId = 12, + SortOrder = 5, + Status = "Active" + }, + new + { + SubNavItemId = 47, + Code = "accounts.bank-deposits", + Href = "/dashboard/accounts/bank-deposits", + Label = "Bank Deposits", + NavItemId = 12, + SortOrder = 6, + Status = "Active" + }, + new { SubNavItemId = 27, Code = "production.templates", @@ -7245,6 +7811,25 @@ namespace ERPCore.Migrations .IsRequired(); }); + modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b => + { + b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Creator"); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b => { b.HasOne("ERPCore.Domain.Entities.Item", "Item") @@ -7415,6 +8000,17 @@ namespace ERPCore.Migrations b.Navigation("ParentDepartment"); }); + modelBuilder.Entity("ERPCore.Domain.Entities.DocumentApproval", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Decider") + .WithMany() + .HasForeignKey("DecidedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Decider"); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b => { b.HasOne("ERPCore.Domain.Entities.Branch", "Branch") @@ -7746,6 +8342,24 @@ namespace ERPCore.Migrations b.Navigation("Item"); }); + modelBuilder.Entity("ERPCore.Domain.Entities.ItemPriceHistory", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PriceAdjustment", "PriceAdjustment") + .WithMany() + .HasForeignKey("PriceAdjustmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Item"); + + b.Navigation("PriceAdjustment"); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b => { b.HasOne("ERPCore.Domain.Entities.Item", "Item") @@ -7985,6 +8599,36 @@ namespace ERPCore.Migrations b.Navigation("Warehouse"); }); + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustment", b => + { + b.HasOne("ERPCore.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustmentLine", b => + { + b.HasOne("ERPCore.Domain.Entities.Item", "Item") + .WithMany() + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ERPCore.Domain.Entities.PriceAdjustment", "PriceAdjustment") + .WithMany("Lines") + .HasForeignKey("PriceAdjustmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PriceAdjustment"); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.ProductConfig", b => { b.HasOne("ERPCore.Domain.Entities.User", "UpdatedByUser") @@ -9377,6 +10021,16 @@ namespace ERPCore.Migrations b.Navigation("Lines"); }); + modelBuilder.Entity("ERPCore.Domain.Entities.Expense", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("ERPCore.Domain.Entities.ExpenseCategory", b => + { + b.Navigation("Titles"); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b => { b.Navigation("Lines"); @@ -9419,6 +10073,11 @@ namespace ERPCore.Migrations b.Navigation("Lines"); }); + modelBuilder.Entity("ERPCore.Domain.Entities.PriceAdjustment", b => + { + b.Navigation("Lines"); + }); + modelBuilder.Entity("ERPCore.Domain.Entities.ProductionRun", b => { b.Navigation("Edges"); diff --git a/Backend/ERPCore/Program.cs b/Backend/ERPCore/Program.cs index fe20411..9a74631 100644 --- a/Backend/ERPCore/Program.cs +++ b/Backend/ERPCore/Program.cs @@ -28,7 +28,7 @@ builder.Host.UseSerilog((ctx, cfg) => cfg .WriteTo.File("logs/erpcore-.log", rollingInterval: RollingInterval.Day)); // Controllers + JSON: serialize enums as their string names (docs/11 §8, camelCase). -builder.Services.AddControllers() +builder.Services.AddControllers(o => o.Filters.Add()) .AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); // EF Core + MariaDB (Pomelo). The server version is pinned explicitly rather than using @@ -149,6 +149,19 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +// Accounts: expenses (docs/EXPENSE_MODULE_PLAN.md). ExpenseService is also registered as itself: +// ExpensePaymentService reuses its post step so Post & Pay is one transaction. +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(sp => sp.GetRequiredService()); +builder.Services.AddScoped(); + +// Accounts: bank deposits of till cash (docs/BANK_DEPOSIT_MODULE_PLAN.md). The business-day clock +// is shared with the Day-End Sales Report so both agree on what "today" is. +builder.Services.AddSingleton(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + // Sales (Phase 1) builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -163,16 +176,27 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Stock transactions + reference data (docs/11 §5–6) builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); +// AdjustmentService is also registered as itself: CountService submits its variances through it. +builder.Services.AddScoped(); +builder.Services.AddScoped(sp => sp.GetRequiredService()); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +// Low-stock alerts: FIFO issues record into the tracker; the global LowStockAlertFilter checks +// them against reorder points after each successful request and raises "stock.low". +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); // Cross-cutting: audit trail + GL-ready journal read access (docs/11 §6 / FR-X-02, FR-STK-13) diff --git a/Backend/ERPCore/Services/AdjustmentService.cs b/Backend/ERPCore/Services/AdjustmentService.cs index 7791412..3642694 100644 --- a/Backend/ERPCore/Services/AdjustmentService.cs +++ b/Backend/ERPCore/Services/AdjustmentService.cs @@ -1,9 +1,11 @@ +using System.Text.Json; using ERPCore.Domain; using ERPCore.Domain.Entities; using ERPCore.Domain.Enums; using ERPCore.Dtos.Common; using ERPCore.Dtos.Stock; using ERPCore.Infra.Auth; +using ERPCore.Infra.Persistence; using ERPCore.Infra.UoW; using ERPCore.Repositories.Interfaces; using ERPCore.Services.Gl; @@ -15,27 +17,36 @@ using Microsoft.EntityFrameworkCore; namespace ERPCore.Services; /// -/// Stock-adjustment service — the highest-risk feature (02-SECURITY C.5). Auto-posts -/// with a mandatory reason code and user stamp. Line application (FIFO consume on a -/// decrease, layer create on an increase) + ledger posting is delegated to -/// . Runs in a single UoW transaction (NFR-02/05). Also posts -/// a real GL journal entry — increases and decreases post as separate Inventory/Gain and -/// Loss/Inventory lines respectively (never netted against each other), so a count that's -/// simultaneously over on one item and under on another shows both, not a false net. +/// Stock-adjustment service — the highest-risk feature (02-SECURITY C.5). An adjustment is a +/// request with a mandatory reason code; it moves stock only once approved by one or two people +/// other than the requester (docs/DUAL_APPROVAL_PLAN.md §5, rules in ). +/// With approvals switched off it posts on create, as it always used to. Posting — FIFO consume on a +/// decrease, layer create on an increase, ledger, serial moves and the GL journal — is one +/// transaction (NFR-02/05) and is re-validated at approval time, since stock and serials may have +/// moved while the request waited. Increases and decreases post as separate Inventory/Gain and +/// Loss/Inventory GL lines (never netted), so a count over on one item and under on another shows both. /// public sealed class AdjustmentService : IAdjustmentService { + public const string ApprovalDocType = "ADJ"; + public const string ApprovalNavCode = "stock.adjustments"; + + private static readonly IReadOnlyCollection WriteOffStatuses = [SerialStatus.InStock, SerialStatus.Damaged]; + private readonly IRepository _adjustments; private readonly IRepository _warehouses; private readonly IRepository _items; private readonly IRepository _reasonCodes; private readonly IRepository _ledger; private readonly IStockMutator _mutator; + private readonly IFifoCostingService _fifo; private readonly INumberSequenceService _numbers; private readonly ICurrentUser _currentUser; private readonly IBranchConfigService _branchConfig; private readonly INotificationService _notifications; + private readonly IApprovalService _approvals; private readonly IUnitOfWork _uow; + private readonly ErpDbContext _db; private readonly ISerialTrackingService _serialTracking; private readonly IGeneralLedgerService _gl; private readonly IGlAccountConfigService _glAccounts; @@ -53,18 +64,19 @@ public sealed class AdjustmentService : IAdjustmentService _reasonCodes = reasonCodes; _ledger = ledger; _mutator = mutator; + _fifo = fifo; _numbers = numbers; _currentUser = currentUser; _branchConfig = branchConfig; _notifications = notifications; + _approvals = approvals; _uow = uow; + _db = db; _gl = gl; _glAccounts = glAccounts; } - /// Builds and posts the GL journal for a 's stock movement. - /// 's variance posting mirrors this exact logic for the same reason - /// (it creates a through the same call). + /// Builds and posts the GL journal for a 's stock movement. private async Task PostAdjustmentJournalAsync( string docNo, DateTime now, IReadOnlyList refs, CancellationToken ct) { @@ -99,7 +111,7 @@ public sealed class AdjustmentService : IAdjustmentService } public async Task> ListAsync( - PageQuery query, int? warehouseId, int? branchId, int? reasonCodeId, CancellationToken ct = default) + PageQuery query, int? warehouseId, int? branchId, int? reasonCodeId, AdjustmentStatus? status = null, CancellationToken ct = default) { var q = _adjustments.Query().AsNoTracking(); @@ -112,13 +124,15 @@ public sealed class AdjustmentService : IAdjustmentService // Warehouses are shared across branches — the adjustment carries its own owning BranchId. if (branchId is not null) q = q.Where(a => a.BranchId == branchId); if (reasonCodeId is not null) q = q.Where(a => a.ReasonCodeId == reasonCodeId); + if (status is not null) q = q.Where(a => a.Status == status); var total = await q.CountAsync(ct); var rows = await q.OrderByDescending(a => a.AdjustmentId) .Skip(query.Skip).Take(query.PageSize) .Select(a => new AdjustmentSummaryDto( a.AdjustmentId, a.DocNo, a.WarehouseId, a.BranchId, a.ReasonCodeId, a.Status, - a.CreatedBy, a.CreatedAt, a.Lines.Count, a.GlJournalNo)) + a.CreatedBy, a.CreatedAt, a.Lines.Count, a.GlJournalNo, a.RequiredApprovals, + a.Status == AdjustmentStatus.PendingApproval ? 1 : a.Status == AdjustmentStatus.PendingSecondApproval ? 2 : null)) .ToListAsync(ct); return PagedResponse.Create(rows, query.Page, query.PageSize, total); @@ -129,20 +143,22 @@ public sealed class AdjustmentService : IAdjustmentService var adjustment = await _adjustments.Query().AsNoTracking() .Include(a => a.Lines) .FirstOrDefaultAsync(a => a.AdjustmentId == adjustmentId, ct); - if (adjustment is null) return null; - - // The ledger reference is polymorphic (docs/10 C.9) — there is no FK to follow, - // so the refs this adjustment posted are recovered by source-doc lookup. - var ledgerRefs = await _ledger.Query().AsNoTracking() - .Where(l => l.SourceDocType == DocumentTypes.Adjustment && l.SourceDocId == adjustmentId) - .OrderBy(l => l.LedgerId) - .Select(l => l.LedgerId) - .ToListAsync(ct); - - return ToDto(adjustment, ledgerRefs); + return adjustment is null ? null : await ToDtoAsync(adjustment, ct); } public async Task CreateAsync(CreateAdjustmentRequest request, CancellationToken ct = default) + { + var (adjustment, refs) = await SubmitAsync(request, null, ct); + return await ToDtoAsync(adjustment, ct, refs.Select(l => l.LedgerId).ToList()); + } + + /// + /// Validates and saves an adjustment request; posts it straight away when no approval is + /// required. lets a caller (a stock count) commit its own + /// change together with the adjustment. Returns the ledger entries posted (none while pending). + /// + internal async Task<(StockAdjustment Adjustment, IReadOnlyList Refs)> SubmitAsync( + CreateAdjustmentRequest request, Func? alsoInTransaction, CancellationToken ct) { if (request.ReasonCodeId is null) throw new DomainException(ErrorCodes.ReasonCodeRequired, "A reason code is required for an adjustment.", 400); @@ -157,77 +173,300 @@ public sealed class AdjustmentService : IAdjustmentService if (reason.Context != ReasonContext.Adjustment) throw new DomainException(ErrorCodes.Validation, $"Reason code {request.ReasonCodeId} is not an Adjustment reason.", 422); - // Serials per line: outbound ones are validated as on hand; inbound ones are created (or - // re-found) in the context now and get their ids on the flush below. - var serialsByLine = new List>(); - var documentSerials = new HashSet(); - var documentSerialNos = new HashSet(StringComparer.OrdinalIgnoreCase); + // Validate every line (and its serials) now, so a bad request is refused up front rather + // than at approval time. The value drives the approval threshold. + var value = 0m; foreach (var line in request.Lines) { if (line.QtyDelta == 0) throw new DomainException(ErrorCodes.Validation, "qtyDelta cannot be zero.", 422); var item = await _items.Query().AsNoTracking().FirstOrDefaultAsync(i => i.ItemId == line.ItemId, ct) ?? throw new DomainException(ErrorCodes.Validation, $"Item {line.ItemId} does not exist.", 422); - serialsByLine.Add(line.QtyDelta < 0 - ? await _serialTracking.ResolveOutboundAsync(item, request.WarehouseId, -line.QtyDelta, line.SerialIds, - documentSerials, new[] { SerialStatus.InStock, SerialStatus.Damaged }, ct) - : await _serialTracking.ResolveInboundAsync(item, request.WarehouseId, line.QtyDelta, line.SerialNos, documentSerialNos, ct)); + value += Math.Abs(line.QtyDelta) * await UnitValueAsync(line.ItemId, request.WarehouseId, ct); } + await ResolveSerialsAsync(request.WarehouseId, request.Lines.Select(ToPlan).ToList(), ct); + // Validated only: posting resolves them again and creates any new ones exactly once. + DiscardUnsavedSerials(); + + var required = await _approvals.RequiredForStockAdjustmentAsync(value, ct); var now = DateTime.UtcNow; - - var (adjustment, ledgerRefs) = await _uow.ExecuteInTransactionAsync(async token => + var (adjustment, refs) = await _uow.ExecuteInTransactionAsync(async token => { - var docNo = await _numbers.NextAsync(DocumentTypes.Adjustment, token); var entity = new StockAdjustment { - DocNo = docNo, + DocNo = await _numbers.NextAsync(DocumentTypes.Adjustment, token), WarehouseId = request.WarehouseId, BranchId = branchId, ReasonCodeId = request.ReasonCodeId.Value, - Status = AdjustmentStatus.Posted, + Status = required == 0 ? AdjustmentStatus.Posted : AdjustmentStatus.PendingApproval, + RequiredApprovals = required, CreatedBy = _currentUser.AuditUserId, CreatedAt = now, - Lines = request.Lines.Select((l, i) => new StockAdjustmentLine + Lines = request.Lines.Select(l => new StockAdjustmentLine { ItemId = l.ItemId, BinId = l.BinId, BatchId = l.BatchId, QtyDelta = l.QtyDelta, - Serial = serialsByLine[i].Count == 1 ? serialsByLine[i][0] : null + RequestedSerialIds = l.SerialIds is { Count: > 0 } ? JsonSerializer.Serialize(l.SerialIds) : null, + RequestedSerialNos = l.SerialNos is { Count: > 0 } ? JsonSerializer.Serialize(l.SerialNos) : null }).ToList() }; await _adjustments.AddAsync(entity, token); - await _uow.SaveChangesAsync(token); // flush so AdjustmentId (and new SerialIds) are valid - var deltas = request.Lines.Select((l, i) => new StockDelta(l.ItemId, l.BinId, l.BatchId, l.QtyDelta, - serialsByLine[i].Select(s => s.SerialId).ToList())).ToList(); - var refs = await _mutator.ApplyAsync(request.WarehouseId, DocumentTypes.Adjustment, entity.AdjustmentId, now, deltas, token); - - for (var i = 0; i < request.Lines.Count; i++) + IReadOnlyList posted = []; + if (required == 0) { - var line = request.Lines[i]; - await _serialTracking.MoveAsync(serialsByLine[i], - line.QtyDelta < 0 ? SerialStatus.Scrapped : SerialStatus.InStock, - line.QtyDelta < 0 ? null : request.WarehouseId, line.QtyDelta < 0 ? null : line.BinId, - DocumentTypes.Adjustment, entity.AdjustmentId, now, token); + await _uow.SaveChangesAsync(token); // flush so AdjustmentId is a valid ledger sourceDocId + posted = await PostInCurrentTransactionAsync(entity, token); } - - entity.GlJournalNo = await PostAdjustmentJournalAsync(docNo, now, refs, token); - if (entity.GlJournalNo is not null) entity.GlPostedAt = now; - - return (entity, refs); + if (alsoInTransaction is not null) await alsoInTransaction(token); + return (entity, posted); }, ct); - await _notifications.RaiseAsync("stock.adjustment.created", "Stock Adjustment Created", $"Stock adjustment {adjustment.DocNo} was created.", nameof(StockAdjustment), adjustment.AdjustmentId, ct); - - return ToDto(adjustment, ledgerRefs.Select(l => l.LedgerId).ToList()); + if (required == 0) + { + await _notifications.RaiseAsync("stock.adjustment.created", "Stock Adjustment Created", $"Stock adjustment {adjustment.DocNo} was created.", nameof(StockAdjustment), adjustment.AdjustmentId, ct); + } + else + { + await _approvals.NotifyPendingAsync(Target(adjustment), await SummaryAsync(adjustment, ct), ct); + } + return (adjustment, refs); } - private static AdjustmentDto ToDto(StockAdjustment a, IReadOnlyList ledgerRefs) => new( - a.AdjustmentId, a.DocNo, a.WarehouseId, a.BranchId, a.ReasonCodeId, a.Status, a.CreatedBy, a.CreatedAt, - a.GlJournalNo, a.GlPostedAt, - a.Lines.OrderBy(l => l.AdjLineId) - .Select(l => new AdjustmentLineDto(l.AdjLineId, l.ItemId, l.BinId, l.BatchId, l.QtyDelta, l.SerialId)).ToList(), - ledgerRefs); + public async Task ApproveAsync(int adjustmentId, string? comment, CancellationToken ct = default) + { + var adjustment = await LoadPendingAsync(adjustmentId, ct); + var target = Target(adjustment); + var now = DateTime.UtcNow; + + var outcome = await _approvals.DecideAsync(target, approve: true, comment, ct); + IReadOnlyList refs = []; + try + { + refs = await _uow.ExecuteInTransactionAsync(async token => + { + if (target.CurrentLevel == 1) + { + adjustment.ApprovedLevel1By = _currentUser.AuditUserId; + adjustment.ApprovedLevel1At = now; + } + else + { + adjustment.ApprovedLevel2By = _currentUser.AuditUserId; + adjustment.ApprovedLevel2At = now; + } + + if (outcome == ApprovalOutcome.AdvancedToLevel2) + { + adjustment.Status = AdjustmentStatus.PendingSecondApproval; + return (IReadOnlyList)[]; + } + + adjustment.Status = AdjustmentStatus.Posted; + return await PostInCurrentTransactionAsync(adjustment, token); + }, ct); + } + catch (DbUpdateConcurrencyException) + { + throw new DomainException(ErrorCodes.ApprovalNotPending, $"Stock adjustment {adjustment.DocNo} was just decided by someone else — reload it.", 409); + } + + if (outcome == ApprovalOutcome.AdvancedToLevel2) + { + await _approvals.NotifyPendingAsync(Target(adjustment), await SummaryAsync(adjustment, ct), ct); + } + else + { + await _notifications.NotifyUsersAsync([adjustment.CreatedBy], "approval.approved", + $"Approved: {adjustment.DocNo}", $"Stock adjustment {adjustment.DocNo} was approved and posted to stock.", + ApprovalDocType, adjustment.AdjustmentId, ct); + await _notifications.RaiseAsync("stock.adjustment.created", "Stock Adjustment Created", $"Stock adjustment {adjustment.DocNo} was posted.", nameof(StockAdjustment), adjustment.AdjustmentId, ct); + } + + return await ToDtoAsync(adjustment, ct, refs.Select(r => r.LedgerId).ToList()); + } + + public async Task RejectAsync(int adjustmentId, string reason, CancellationToken ct = default) + { + var adjustment = await LoadPendingAsync(adjustmentId, ct); + await _approvals.DecideAsync(Target(adjustment), approve: false, reason, ct); + + adjustment.Status = AdjustmentStatus.Rejected; + adjustment.RejectedBy = _currentUser.AuditUserId; + adjustment.RejectedAt = DateTime.UtcNow; + adjustment.RejectReason = reason.Trim(); + await SaveDecisionAsync(adjustment, ct); + + await _notifications.NotifyUsersAsync([adjustment.CreatedBy], "approval.rejected", + $"Rejected: {adjustment.DocNo}", $"Stock adjustment {adjustment.DocNo} was rejected: {adjustment.RejectReason}", + ApprovalDocType, adjustment.AdjustmentId, ct); + return await ToDtoAsync(adjustment, ct); + } + + public async Task CancelAsync(int adjustmentId, CancellationToken ct = default) + { + var adjustment = await LoadPendingAsync(adjustmentId, ct); + if (adjustment.CreatedBy != _currentUser.AuditUserId) + throw new DomainException(ErrorCodes.ApprovalForbidden, "Only the person who made the request can withdraw it.", 403); + + adjustment.Status = AdjustmentStatus.Cancelled; + adjustment.CancelledAt = DateTime.UtcNow; + await SaveDecisionAsync(adjustment, ct); + return await ToDtoAsync(adjustment, ct); + } + + // --- Posting --------------------------------------------------------------------------------- + + /// What a line asks for, from a request or from a saved pending line. + private sealed record LinePlan(int ItemId, int? BinId, int? BatchId, decimal QtyDelta, List? SerialIds, List? SerialNos); + + private static LinePlan ToPlan(CreateAdjustmentLineInput l) => new(l.ItemId, l.BinId, l.BatchId, l.QtyDelta, l.SerialIds, l.SerialNos); + + private static LinePlan ToPlan(StockAdjustmentLine l) => new( + l.ItemId, l.BinId, l.BatchId, l.QtyDelta, + l.RequestedSerialIds is null ? null : JsonSerializer.Deserialize>(l.RequestedSerialIds), + l.RequestedSerialNos is null ? null : JsonSerializer.Deserialize>(l.RequestedSerialNos)); + + /// + /// Outbound serials must be at the warehouse (InStock or Damaged); inbound numbers are new or + /// out of stock (new ones are added to the context, unsaved). Same rules on request and posting. + /// + private async Task>> ResolveSerialsAsync(int warehouseId, IReadOnlyList lines, CancellationToken ct) + { + var result = new List>(); + var documentSerials = new HashSet(); + var documentSerialNos = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var line in lines) + { + var item = await _items.Query().AsNoTracking().FirstAsync(i => i.ItemId == line.ItemId, ct); + result.Add(line.QtyDelta < 0 + ? await _serialTracking.ResolveOutboundAsync(item, warehouseId, -line.QtyDelta, line.SerialIds, documentSerials, WriteOffStatuses, ct) + : await _serialTracking.ResolveInboundAsync(item, warehouseId, line.QtyDelta, line.SerialNos, documentSerialNos, ct)); + } + return result; + } + + /// Drops serials a pending request's validation added, so they aren't created early. + private void DiscardUnsavedSerials() + { + foreach (var entry in _db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).ToList()) + entry.State = EntityState.Detached; + } + + /// + /// Applies a saved adjustment to stock: re-resolves its serials, FIFO-consumes / adds layers, + /// writes the ledger, moves serials and posts the GL journal. Runs inside the caller's transaction; + /// throws (rolling everything back) if stock or serials no longer allow it. + /// + private async Task> PostInCurrentTransactionAsync(StockAdjustment entity, CancellationToken ct) + { + var now = DateTime.UtcNow; + var lines = entity.Lines.OrderBy(l => l.AdjLineId).ToList(); + var plans = lines.Select(ToPlan).ToList(); + var serialsByLine = await ResolveSerialsAsync(entity.WarehouseId, plans, ct); + for (var i = 0; i < lines.Count; i++) + lines[i].Serial = serialsByLine[i].Count == 1 ? serialsByLine[i][0] : null; + await _uow.SaveChangesAsync(ct); // new serials get their ids + + var deltas = plans.Select((l, i) => new StockDelta(l.ItemId, l.BinId, l.BatchId, l.QtyDelta, + serialsByLine[i].Select(s => s.SerialId).ToList())).ToList(); + var refs = await _mutator.ApplyAsync(entity.WarehouseId, DocumentTypes.Adjustment, entity.AdjustmentId, now, deltas, ct); + + for (var i = 0; i < plans.Count; i++) + { + var line = plans[i]; + await _serialTracking.MoveAsync(serialsByLine[i], + line.QtyDelta < 0 ? SerialStatus.Scrapped : SerialStatus.InStock, + line.QtyDelta < 0 ? null : entity.WarehouseId, line.QtyDelta < 0 ? null : line.BinId, + DocumentTypes.Adjustment, entity.AdjustmentId, now, ct); + } + + entity.GlJournalNo = await PostAdjustmentJournalAsync(entity.DocNo, now, refs, ct); + if (entity.GlJournalNo is not null) entity.GlPostedAt = now; + entity.PostedAt = now; + return refs; + } + + // --- Helpers --------------------------------------------------------------------------------- + + /// Average FIFO cost per unit at the warehouse — prices the request for the approval threshold. + private async Task UnitValueAsync(int itemId, int warehouseId, CancellationToken ct) + { + var valuation = await _fifo.GetValuationAsync(itemId, warehouseId, ct); + return valuation.TotalQty > 0 ? valuation.TotalValue / valuation.TotalQty : 0m; + } + + private async Task LoadPendingAsync(int adjustmentId, CancellationToken ct) + { + var adjustment = await _adjustments.Query().Include(a => a.Lines) + .FirstOrDefaultAsync(a => a.AdjustmentId == adjustmentId, ct) + ?? throw new NotFoundException($"Stock adjustment {adjustmentId} was not found."); + if (adjustment.Status is not (AdjustmentStatus.PendingApproval or AdjustmentStatus.PendingSecondApproval)) + throw new DomainException(ErrorCodes.ApprovalNotPending, $"Stock adjustment {adjustment.DocNo} is {adjustment.Status} and not waiting for approval.", 409); + return adjustment; + } + + private async Task SaveDecisionAsync(StockAdjustment adjustment, CancellationToken ct) + { + try + { + await _uow.SaveChangesAsync(ct); + } + catch (DbUpdateConcurrencyException) + { + throw new DomainException(ErrorCodes.ApprovalNotPending, $"Stock adjustment {adjustment.DocNo} was just decided by someone else — reload it.", 409); + } + } + + private static int? PendingLevel(StockAdjustment a) => a.Status switch + { + AdjustmentStatus.PendingApproval => 1, + AdjustmentStatus.PendingSecondApproval => 2, + _ => null + }; + + private static ApprovalTarget Target(StockAdjustment a) => new( + ApprovalDocType, a.AdjustmentId, a.DocNo, ApprovalNavCode, a.CreatedBy, a.RequiredApprovals, + PendingLevel(a) ?? 1, a.ApprovedLevel1By); + + private async Task SummaryAsync(StockAdjustment a, CancellationToken ct) + { + var warehouse = await _warehouses.Query().AsNoTracking().Where(w => w.WarehouseId == a.WarehouseId).Select(w => w.Code).FirstOrDefaultAsync(ct); + var reason = await _reasonCodes.Query().AsNoTracking().Where(r => r.ReasonCodeId == a.ReasonCodeId).Select(r => r.Description).FirstOrDefaultAsync(ct); + return $"Stock adjustment {a.DocNo} ({a.Lines.Count} line{(a.Lines.Count == 1 ? "" : "s")}, {warehouse}, {reason})"; + } + + private async Task ToDtoAsync(StockAdjustment a, CancellationToken ct, IReadOnlyList? ledgerRefs = null) + { + // The ledger reference is polymorphic (docs/10 C.9) — there is no FK to follow, + // so the refs this adjustment posted are recovered by source-doc lookup. + ledgerRefs ??= await _ledger.Query().AsNoTracking() + .Where(l => l.SourceDocType == DocumentTypes.Adjustment && l.SourceDocId == a.AdjustmentId) + .OrderBy(l => l.LedgerId) + .Select(l => l.LedgerId) + .ToListAsync(ct); + + var pending = PendingLevel(a); + var whyNot = pending is null ? "Not waiting for approval." : await _approvals.WhyCannotDecideAsync(Target(a), ct); + + return new AdjustmentDto( + a.AdjustmentId, a.DocNo, a.WarehouseId, a.BranchId, a.ReasonCodeId, a.Status, a.CreatedBy, a.CreatedAt, + a.GlJournalNo, a.GlPostedAt, + a.Lines.OrderBy(l => l.AdjLineId).Select(l => + { + var plan = ToPlan(l); + return new AdjustmentLineDto(l.AdjLineId, l.ItemId, l.BinId, l.BatchId, l.QtyDelta, l.SerialId, plan.SerialIds, plan.SerialNos); + }).ToList(), + ledgerRefs, + a.RequiredApprovals, pending, + a.ApprovedLevel1By, a.ApprovedLevel1At, a.ApprovedLevel2By, a.ApprovedLevel2At, + a.RejectedBy, a.RejectedAt, a.RejectReason, a.PostedAt, + await _approvals.HistoryAsync(ApprovalDocType, a.AdjustmentId, ct), + whyNot is null, whyNot, + pending is not null && a.CreatedBy == _currentUser.AuditUserId, + await _db.Users.AsNoTracking().Where(u => u.UserId == a.CreatedBy).Select(u => u.DisplayName).FirstOrDefaultAsync(ct)); + } } diff --git a/Backend/ERPCore/Services/ApprovalInboxService.cs b/Backend/ERPCore/Services/ApprovalInboxService.cs new file mode 100644 index 0000000..89884ff --- /dev/null +++ b/Backend/ERPCore/Services/ApprovalInboxService.cs @@ -0,0 +1,93 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Approvals; +using ERPCore.Infra.Auth; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// The Approvals inbox (docs/DUAL_APPROVAL_PLAN.md §10): every pending stock and price adjustment +/// the current user can approve, plus their own requests (shown as waiting for others). A branch +/// user only sees their branch's stock adjustments; price changes are company-wide. +/// +public sealed class ApprovalInboxService +{ + private readonly IRepository _stock; + private readonly IRepository _prices; + private readonly IApprovalService _approvals; + private readonly INavPermissionService _nav; + private readonly ICurrentUser _currentUser; + private readonly ICurrentBranchContext _branch; + + public ApprovalInboxService( + IRepository stock, IRepository prices, IApprovalService approvals, + INavPermissionService nav, ICurrentUser currentUser, ICurrentBranchContext branch) + { + _stock = stock; + _prices = prices; + _approvals = approvals; + _nav = nav; + _currentUser = currentUser; + _branch = branch; + } + + public async Task PendingAsync(CancellationToken ct = default) + { + var me = _currentUser.AuditUserId; + var canStock = await CanApproveScreenAsync(AdjustmentService.ApprovalNavCode, ct); + var canPrice = await CanApproveScreenAsync(PriceAdjustmentService.ApprovalNavCode, ct); + var items = new List(); + + var stockQuery = _stock.Query().AsNoTracking() + .Where(a => a.Status == AdjustmentStatus.PendingApproval || a.Status == AdjustmentStatus.PendingSecondApproval) + .Where(a => canStock || a.CreatedBy == me); + if (_branch.IsActive) stockQuery = stockQuery.Where(a => a.BranchId == _branch.BranchId); + var stock = await stockQuery + .Select(a => new + { + a.AdjustmentId, a.DocNo, a.Status, a.RequiredApprovals, a.CreatedBy, a.CreatedAt, a.ApprovedLevel1By, + RequestedByName = a.Creator!.DisplayName, Warehouse = a.Warehouse!.Code, Reason = a.ReasonCode!.Description, Lines = a.Lines.Count + }) + .ToListAsync(ct); + foreach (var a in stock) + { + var level = a.Status == AdjustmentStatus.PendingApproval ? 1 : 2; + var target = new ApprovalTarget(AdjustmentService.ApprovalDocType, a.AdjustmentId, a.DocNo, AdjustmentService.ApprovalNavCode, + a.CreatedBy, a.RequiredApprovals, level, a.ApprovedLevel1By); + var why = canStock ? await _approvals.WhyCannotDecideAsync(target, ct) : "Waiting for an approver."; + items.Add(new PendingApprovalDto(AdjustmentService.ApprovalDocType, a.AdjustmentId, a.DocNo, + $"Stock adjustment — {a.Lines} line{(a.Lines == 1 ? "" : "s")}, {a.Warehouse}, {a.Reason}", + level, a.RequiredApprovals, a.CreatedBy, a.RequestedByName, a.CreatedAt, why is null, why)); + } + + var prices = await _prices.Query().AsNoTracking() + .Where(p => p.Status == PriceAdjustmentStatus.PendingApproval || p.Status == PriceAdjustmentStatus.PendingSecondApproval) + .Where(p => canPrice || p.CreatedBy == me) + .Select(p => new + { + p.PriceAdjustmentId, p.DocNo, p.Status, p.RequiredApprovals, p.CreatedBy, p.CreatedAt, p.ApprovedLevel1By, + RequestedByName = p.Creator!.DisplayName, p.Reason, Lines = p.Lines.Count + }) + .ToListAsync(ct); + foreach (var p in prices) + { + var level = p.Status == PriceAdjustmentStatus.PendingApproval ? 1 : 2; + var target = new ApprovalTarget(PriceAdjustmentService.ApprovalDocType, p.PriceAdjustmentId, p.DocNo, PriceAdjustmentService.ApprovalNavCode, + p.CreatedBy, p.RequiredApprovals, level, p.ApprovedLevel1By); + var why = canPrice ? await _approvals.WhyCannotDecideAsync(target, ct) : "Waiting for an approver."; + items.Add(new PendingApprovalDto(PriceAdjustmentService.ApprovalDocType, p.PriceAdjustmentId, p.DocNo, + $"Price adjustment — {p.Lines} item{(p.Lines == 1 ? "" : "s")}: {p.Reason}", + level, p.RequiredApprovals, p.CreatedBy, p.RequestedByName, p.CreatedAt, why is null, why)); + } + + // Actionable first, then oldest first — the longest-waiting request is what needs attention. + var ordered = items.OrderByDescending(i => i.CanApprove).ThenBy(i => i.RequestedAt).ToList(); + return new PendingApprovalsDto(ordered, ordered.Count(i => i.CanApprove)); + } + + private async Task CanApproveScreenAsync(string navCode, CancellationToken ct) + => _currentUser.IsAdmin || await _nav.HasActionAsync(_currentUser.RoleCode, navCode, RolePermissionAction.Approve, ct); +} diff --git a/Backend/ERPCore/Services/ApprovalService.cs b/Backend/ERPCore/Services/ApprovalService.cs new file mode 100644 index 0000000..de121c1 --- /dev/null +++ b/Backend/ERPCore/Services/ApprovalService.cs @@ -0,0 +1,178 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Approvals; +using ERPCore.Infra.Auth; +using ERPCore.Infra.UoW; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Shared approval engine for stock and price adjustments (docs/DUAL_APPROVAL_PLAN.md §4). Holds the +/// maker–checker rules in one place; the document services call and apply +/// their effect only on . There is no Admin bypass of the +/// two-person rule — an Admin can approve, but never their own request and never both levels. +/// +public sealed class ApprovalService : IApprovalService +{ + private const string AdminRoleCode = "Admin"; + + private readonly IRepository _config; + private readonly IRepository _approvals; + private readonly IRepository _users; + private readonly IRepository _rolePermissions; + private readonly ICurrentUser _currentUser; + private readonly INotificationService _notifications; + private readonly IUnitOfWork _uow; + + public ApprovalService( + IRepository config, IRepository approvals, IRepository users, + IRepository rolePermissions, ICurrentUser currentUser, INotificationService notifications, IUnitOfWork uow) + { + _config = config; + _approvals = approvals; + _users = users; + _rolePermissions = rolePermissions; + _currentUser = currentUser; + _notifications = notifications; + _uow = uow; + } + + public async Task GetConfigAsync(CancellationToken ct = default) => Map(await LoadConfigAsync(ct)); + + public async Task UpdateConfigAsync(UpdateApprovalConfigRequest request, CancellationToken ct = default) + { + var config = await _config.Query().FirstOrDefaultAsync(c => c.ApprovalConfigId == ApprovalConfig.SingletonId, ct); + if (config is null) + { + config = new ApprovalConfig { ApprovalConfigId = ApprovalConfig.SingletonId }; + await _config.AddAsync(config, ct); + } + + config.StockAdjustmentLevels = Math.Clamp(request.StockAdjustmentLevels, 0, 2); + config.StockAdjustmentLevel2ThresholdValue = Math.Max(0m, request.StockAdjustmentLevel2ThresholdValue); + config.PriceAdjustmentLevels = Math.Clamp(request.PriceAdjustmentLevels, 0, 2); + config.PriceAdjustmentLevel2ThresholdPercent = Math.Max(0m, request.PriceAdjustmentLevel2ThresholdPercent); + var roles = request.Level2RoleCodes.Select(r => r.Trim()).Where(r => r.Length > 0).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + config.Level2RoleCodes = roles.Count == 0 ? null : string.Join(",", roles); + config.UpdatedAt = DateTime.UtcNow; + config.UpdatedBy = _currentUser.AuditUserId; + + await _uow.SaveChangesAsync(ct); + return Map(config); + } + + public async Task RequiredForStockAdjustmentAsync(decimal absoluteValue, CancellationToken ct = default) + { + var c = await LoadConfigAsync(ct); + return Levels(c.StockAdjustmentLevels, c.StockAdjustmentLevel2ThresholdValue, absoluteValue); + } + + public async Task RequiredForPriceAdjustmentAsync(decimal largestChangePercent, CancellationToken ct = default) + { + var c = await LoadConfigAsync(ct); + return Levels(c.PriceAdjustmentLevels, c.PriceAdjustmentLevel2ThresholdPercent, largestChangePercent); + } + + /// Two levels drop to one below a positive threshold; 0 and 1 are taken as configured. + public static int Levels(int configured, decimal threshold, decimal measure) + => configured >= 2 && threshold > 0 && measure < threshold ? 1 : Math.Clamp(configured, 0, 2); + + public async Task DecideAsync(ApprovalTarget target, bool approve, string? comment, CancellationToken ct = default) + { + var (why, code) = await CheckAsync(target, ct); + if (why is not null) throw new DomainException(code!, why, 403); + + var note = string.IsNullOrWhiteSpace(comment) ? null : comment.Trim(); + if (!approve && note is null) + throw new DomainException(ErrorCodes.ApprovalReasonRequired, "Give a reason for rejecting.", 400); + + await _approvals.AddAsync(new DocumentApproval + { + DocType = target.DocType, + DocId = target.DocId, + Level = target.CurrentLevel, + Decision = approve ? ApprovalDecision.Approved : ApprovalDecision.Rejected, + DecidedBy = _currentUser.AuditUserId, + DecidedAt = DateTime.UtcNow, + Comment = note + }, ct); + + if (!approve) return ApprovalOutcome.Rejected; + return target.CurrentLevel >= target.RequiredApprovals ? ApprovalOutcome.FullyApproved : ApprovalOutcome.AdvancedToLevel2; + } + + public async Task WhyCannotDecideAsync(ApprovalTarget target, CancellationToken ct = default) + => (await CheckAsync(target, ct)).Why; + + private async Task<(string? Why, string? Code)> CheckAsync(ApprovalTarget target, CancellationToken ct) + { + var actor = _currentUser.AuditUserId; + if (actor == target.RequestedBy) + return ("You made this request — someone else has to approve it.", ErrorCodes.ApprovalForbidden); + if (target.CurrentLevel >= 2 && target.Level1ApprovedBy == actor) + return ("You gave the first approval — a different person has to give the second.", ErrorCodes.ApprovalSamePerson); + if (target.CurrentLevel >= 2) + { + var roles = Level2Roles(await LoadConfigAsync(ct)); + if (roles.Count > 0 && !_currentUser.IsAdmin && !roles.Contains(_currentUser.RoleCode ?? string.Empty)) + return ($"Only {string.Join(", ", roles)} can give the second approval.", ErrorCodes.ApprovalRoleRequired); + } + return (null, null); + } + + public async Task> HistoryAsync(string docType, int docId, CancellationToken ct = default) + => await _approvals.Query().AsNoTracking() + .Where(a => a.DocType == docType && a.DocId == docId) + .OrderBy(a => a.DocumentApprovalId) + .Select(a => new ApprovalStepDto(a.Level, a.Decision, a.DecidedBy, a.Decider!.DisplayName, a.DecidedAt, a.Comment)) + .ToListAsync(ct); + + public async Task> ApproverIdsAsync(ApprovalTarget target, CancellationToken ct = default) + { + var permissionCode = "NAV:" + target.NavCode; + var grants = await _rolePermissions.Query().AsNoTracking() + .Where(rp => rp.Permission!.Code == permissionCode) + .Select(rp => new { rp.RoleId, rp.Actions }) + .ToListAsync(ct); + var approverRoleIds = grants.Where(g => g.Actions.HasFlag(RolePermissionAction.Approve)).Select(g => g.RoleId).ToHashSet(); + + var candidates = await _users.Query().AsNoTracking() + .Where(u => u.Status == EntityStatus.Active && u.RoleId != null) + .Select(u => new { u.UserId, RoleId = u.RoleId!.Value, RoleCode = u.Role!.Code }) + .ToListAsync(ct); + + var level2Roles = target.CurrentLevel >= 2 ? Level2Roles(await LoadConfigAsync(ct)) : []; + return candidates + .Where(u => string.Equals(u.RoleCode, AdminRoleCode, StringComparison.OrdinalIgnoreCase) || approverRoleIds.Contains(u.RoleId)) + .Where(u => u.UserId != target.RequestedBy && u.UserId != target.Level1ApprovedBy) + .Where(u => level2Roles.Count == 0 || level2Roles.Contains(u.RoleCode) || string.Equals(u.RoleCode, AdminRoleCode, StringComparison.OrdinalIgnoreCase)) + .Select(u => u.UserId) + .ToList(); + } + + public async Task NotifyPendingAsync(ApprovalTarget target, string summary, CancellationToken ct = default) + { + var approvers = await ApproverIdsAsync(target, ct); + var step = target.RequiredApprovals > 1 ? $" (approval {target.CurrentLevel} of {target.RequiredApprovals})" : string.Empty; + await _notifications.NotifyUsersAsync(approvers, "approval.pending", + $"Approval needed: {target.DocNo}", $"{summary} is waiting for your approval{step}.", target.DocType, target.DocId, ct); + } + + private async Task LoadConfigAsync(CancellationToken ct) + => await _config.Query().AsNoTracking().FirstOrDefaultAsync(c => c.ApprovalConfigId == ApprovalConfig.SingletonId, ct) + ?? new ApprovalConfig { ApprovalConfigId = ApprovalConfig.SingletonId }; + + private static HashSet Level2Roles(ApprovalConfig c) + => (c.Level2RoleCodes ?? string.Empty) + .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + + private static ApprovalConfigDto Map(ApprovalConfig c) => new( + c.StockAdjustmentLevels, c.StockAdjustmentLevel2ThresholdValue, + c.PriceAdjustmentLevels, c.PriceAdjustmentLevel2ThresholdPercent, + Level2Roles(c).ToList(), c.UpdatedAt); +} diff --git a/Backend/ERPCore/Services/BankDepositService.cs b/Backend/ERPCore/Services/BankDepositService.cs new file mode 100644 index 0000000..73a75ad --- /dev/null +++ b/Backend/ERPCore/Services/BankDepositService.cs @@ -0,0 +1,283 @@ +using ERPCore.Domain; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Accounts; +using ERPCore.Dtos.Common; +using ERPCore.Infra.Auth; +using ERPCore.Infra.Persistence; +using ERPCore.Infra.UoW; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Gl; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Till cash taken to the bank (docs/BANK_DEPOSIT_MODULE_PLAN.md §7.2). Saving a deposit posts a +/// real GL entry (Debit the bank account / Credit the cash account) before anything is committed — +/// the same call-GL-before-commit pattern as , so a GL rejection +/// rolls the deposit back. A deposit may not exceed the branch's cash in hand +/// (); the check runs inside the transaction after locking the +/// branch row, so two cashiers can't both deposit the same cash. Cancelling posts the reversing entry. +/// +public sealed class BankDepositService : IBankDepositService +{ + private const string SourceModule = "BANK_DEPOSIT"; + + private readonly IRepository _deposits; + private readonly IRepository _branches; + private readonly ICashInHandService _cash; + private readonly IGeneralLedgerService _gl; + private readonly ICurrentUser _currentUser; + private readonly ICurrentBranchContext _currentBranch; + private readonly INumberSequenceService _numbers; + private readonly IBranchConfigService _branchConfig; + private readonly IUnitOfWork _uow; + private readonly ErpDbContext _db; + private readonly BusinessDayClock _clock; + + public BankDepositService( + IRepository deposits, IRepository branches, ICashInHandService cash, + IGeneralLedgerService gl, ICurrentUser currentUser, ICurrentBranchContext currentBranch, + INumberSequenceService numbers, IBranchConfigService branchConfig, IUnitOfWork uow, + ErpDbContext db, BusinessDayClock clock) + { + _deposits = deposits; + _branches = branches; + _cash = cash; + _gl = gl; + _currentUser = currentUser; + _currentBranch = currentBranch; + _numbers = numbers; + _branchConfig = branchConfig; + _uow = uow; + _db = db; + _clock = clock; + } + + public async Task ListAsync(PageQuery query, BankDepositListFilter filter, CancellationToken ct = default) + { + IQueryable q = _deposits.Query().AsNoTracking().Include(x => x.Branch); + if (!string.IsNullOrWhiteSpace(query.Q)) + { + var term = query.Q.Trim(); + q = q.Where(x => EF.Functions.Like(x.DocNo, $"%{term}%") + || (x.DepositSlipNo != null && EF.Functions.Like(x.DepositSlipNo, $"%{term}%")) + || (x.DepositedBy != null && EF.Functions.Like(x.DepositedBy, $"%{term}%"))); + } + if (filter.From is not null) q = q.Where(x => x.BusinessDate >= filter.From); + if (filter.To is not null) q = q.Where(x => x.BusinessDate <= filter.To); + if (filter.BranchId is not null) q = q.Where(x => x.BranchId == filter.BranchId); + if (filter.BankAccountId is not null) q = q.Where(x => x.ToGlBankAccountId == filter.BankAccountId); + if (filter.Status is not null) q = q.Where(x => x.Status == filter.Status); + + var total = await q.CountAsync(ct); + var totalAmount = await q.SumAsync(x => x.Amount, ct); + var rows = await q.OrderByDescending(x => x.BusinessDate).ThenByDescending(x => x.BankDepositId) + .Skip(query.Skip).Take(query.PageSize).ToListAsync(ct); + + var page = PagedResponse.Create(rows.Select(Map).ToList(), query.Page, query.PageSize, total); + return new BankDepositListDto(page.Items, page.Pagination, totalAmount); + } + + public async Task GetAsync(int bankDepositId, CancellationToken ct = default) + { + var deposit = await _deposits.Query().AsNoTracking().Include(x => x.Branch) + .FirstOrDefaultAsync(x => x.BankDepositId == bankDepositId, ct); + return deposit is null ? null : Map(deposit); + } + + public async Task CreateAsync(CreateBankDepositRequest request, CancellationToken ct = default) + { + if (request.Amount <= 0) + throw new DomainException(ErrorCodes.Validation, "The deposit amount must be greater than zero.", 400); + + var (branchId, cashScope) = await ResolveBranchAsync(request.BranchId, ct); + var branch = await _branches.Query().AsNoTracking().FirstOrDefaultAsync(b => b.BranchId == branchId, ct) + ?? throw new DomainException(ErrorCodes.Validation, $"Branch {branchId} does not exist.", 422); + + var depositDate = ValidateDates(request); + var (cashAccount, bankAccount) = await ResolveAccountsAsync(request, ct); + + var deposit = await _uow.ExecuteInTransactionAsync(async token => + { + await LockBranchAsync(branchId, token); + + // Re-read inside the lock: a deposit committed a moment ago by someone else is now visible. + var available = await _cash.GetAvailableToDepositAsync(request.BusinessDate, cashScope, token); + if (request.Amount > available) + throw new DomainException(ErrorCodes.BankDepositExceedsCashInHand, + $"Amount {request.Amount:N2} exceeds the cash in hand available for {request.BusinessDate:yyyy-MM-dd} ({available:N2}).", 400); + + var docNo = await _numbers.NextAsync(DocumentTypes.BankDeposit, branchId, token); + var narration = $"Cash deposit {docNo} — {request.BusinessDate:yyyy-MM-dd}"; + var period = await _gl.GetPeriodByDateAsync(depositDate, token); + var posted = await _gl.PostJournalEntryAsync(new GlJournalEntryRequest + { + PeriodId = period.PeriodId, + EntryDate = depositDate, + SourceModule = SourceModule, + Reference = docNo, + Narration = narration, + Lines = + [ + new GlJournalEntryLineRequest(bankAccount.GlAccountCode, request.Amount, 0m, narration), + new GlJournalEntryLineRequest(cashAccount.GlAccountCode, 0m, request.Amount, narration) + ] + }, token); + + var now = DateTime.UtcNow; + var entity = new BankDeposit + { + DocNo = docNo, + BranchId = branchId, + BusinessDate = request.BusinessDate, + DepositDate = depositDate, + Amount = request.Amount, + FromGlCashAccountId = cashAccount.AccountId, + FromCashAccountCode = cashAccount.GlAccountCode, + FromCashAccountName = cashAccount.AccountName, + ToGlBankAccountId = bankAccount.AccountId, + ToBankAccountCode = bankAccount.GlAccountCode, + ToBankAccountName = bankAccount.AccountName, + ToBankName = bankAccount.BankName, + ToAccountNumber = bankAccount.AccountNumber, + DepositSlipNo = Normalize(request.DepositSlipNo), + DepositedBy = Normalize(request.DepositedBy), + Remarks = Normalize(request.Remarks), + Status = BankDepositStatus.Posted, + GlJournalNo = posted.JournalNo, + GlPostedAt = now, + CreatedBy = _currentUser.AuditUserId, + CreatedAt = now + }; + await _deposits.AddAsync(entity, token); + return entity; + }, ct); + + return Map(deposit, branch.Name); + } + + public async Task CancelAsync(int bankDepositId, CancelBankDepositRequest request, CancellationToken ct = default) + { + var deposit = await _deposits.Query().Include(x => x.Branch) + .FirstOrDefaultAsync(x => x.BankDepositId == bankDepositId, ct) + ?? throw new NotFoundException($"Bank deposit {bankDepositId} was not found."); + + if (deposit.Status != BankDepositStatus.Posted) + throw new DomainException(ErrorCodes.BankDepositNotCancellable, $"Bank deposit {deposit.DocNo} is already {deposit.Status}.", 409); + + // Cancelling only puts cash back in hand, so there is no balance to check. + var now = DateTime.UtcNow; + await _uow.ExecuteInTransactionAsync(async token => + { + var today = _clock.Today; + var narration = $"Cancellation of cash deposit {deposit.DocNo}"; + var period = await _gl.GetPeriodByDateAsync(today, token); + var reversal = await _gl.PostJournalEntryAsync(new GlJournalEntryRequest + { + PeriodId = period.PeriodId, + EntryDate = today, + SourceModule = SourceModule, + Reference = deposit.DocNo, + Narration = narration, + Lines = + [ + new GlJournalEntryLineRequest(deposit.FromCashAccountCode, deposit.Amount, 0m, narration), + new GlJournalEntryLineRequest(deposit.ToBankAccountCode, 0m, deposit.Amount, narration) + ] + }, token); + + deposit.Status = BankDepositStatus.Cancelled; + deposit.GlReversalJournalNo = reversal.JournalNo; + deposit.CancelReason = request.Reason.Trim(); + deposit.CancelledBy = _currentUser.AuditUserId; + deposit.CancelledAt = now; + return 0; + }, ct); + + return Map(deposit); + } + + /// + /// The branch the deposit belongs to and the branch scope its cash in hand is measured over. A + /// branch user always deposits for their own branch. With branching off every document lives on + /// the one default branch, so the cash is measured across all of them (scope null). + /// + private async Task<(int BranchId, int? CashScope)> ResolveBranchAsync(int requested, CancellationToken ct) + { + if (_currentBranch.IsActive && _currentBranch.BranchId is { } own) + return (own, own); + + var branchId = await _branchConfig.ResolveBranchIdAsync(requested, ct); + return await _branchConfig.IsBranchingEnabledAsync(ct) ? (branchId, branchId) : (branchId, null); + } + + private DateOnly ValidateDates(CreateBankDepositRequest request) + { + var today = _clock.Today; + if (request.BusinessDate > today) + throw new DomainException(ErrorCodes.BankDepositDateInvalid, "The business date cannot be in the future.", 400); + if (_cash.TrackingStartDate is { } start && request.BusinessDate < start) + throw new DomainException(ErrorCodes.BankDepositDateInvalid, + $"Cash in hand is tracked from {start:yyyy-MM-dd}; earlier days cannot be deposited.", 400); + + var depositDate = request.DepositDate ?? request.BusinessDate; + if (depositDate < request.BusinessDate) + throw new DomainException(ErrorCodes.BankDepositDateInvalid, "The deposit date cannot be before the business date.", 400); + // One day of slack, as with payment dates: the client sends its local date. + if (depositDate > today.AddDays(1)) + throw new DomainException(ErrorCodes.BankDepositDateInvalid, "The deposit date cannot be in the future.", 400); + return depositDate; + } + + private async Task<(GlBankAccount Cash, GlBankAccount Bank)> ResolveAccountsAsync(CreateBankDepositRequest request, CancellationToken ct) + { + var accounts = await _gl.ListBankAccountsAsync(ct); + + // GL keeps cash and bank accounts in separate tables with their own ids, so cash account 1 and + // bank account 1 can both exist — an id only means something together with its type. + var cash = accounts.FirstOrDefault(a => a.AccountId == request.FromGlCashAccountId && IsType(a, "Cash")) + ?? throw new DomainException(ErrorCodes.BankDepositCashAccountInvalid, + $"Cash account {request.FromGlCashAccountId} was not found — deposits must come out of a cash account.", 400); + + var bank = accounts.FirstOrDefault(a => a.AccountId == request.ToGlBankAccountId && IsType(a, "Bank")); + if (bank is null) + throw new DomainException(ErrorCodes.BankDepositBankAccountInvalid, + $"Bank account {request.ToGlBankAccountId} was not found — deposits must go into a bank account.", 400); + + return (cash, bank); + } + + private static bool IsType(GlBankAccount account, string type) + => string.Equals(account.AccountType, type, StringComparison.OrdinalIgnoreCase); + + /// + /// Serializes deposits per branch for the rest of the transaction: the second of two concurrent + /// deposits waits here, then sees the first one's committed row when it re-reads the balance. + /// The in-memory test provider has no row locks (or concurrency), so it is skipped there. + /// + private async Task LockBranchAsync(int branchId, CancellationToken ct) + { + if (!_db.Database.IsRelational()) return; + await _db.Database.ExecuteSqlInterpolatedAsync( + $"SELECT `BranchId` FROM `hr_branches` WHERE `BranchId` = {branchId} FOR UPDATE", ct); + } + + private static string? Normalize(string? value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim(); + + /// stands in when the Branch navigation isn't loaded (just created). + private static BankDepositDto Map(BankDeposit x) => Map(x, x.Branch?.Name); + + private static BankDepositDto Map(BankDeposit x, string? branchName) => new( + x.BankDepositId, x.DocNo, x.BranchId, branchName, + x.BusinessDate, x.DepositDate, x.Amount, + x.FromGlCashAccountId, x.FromCashAccountCode, x.FromCashAccountName, + x.ToGlBankAccountId, x.ToBankAccountCode, x.ToBankAccountName, x.ToBankName, x.ToAccountNumber, + x.DepositSlipNo, x.DepositedBy, x.Remarks, + x.Status, x.GlJournalNo, x.GlPostedAt, + x.GlReversalJournalNo, x.CancelReason, x.CancelledAt, + x.CreatedBy, x.CreatedAt); +} diff --git a/Backend/ERPCore/Services/BundleSaleService.cs b/Backend/ERPCore/Services/BundleSaleService.cs index 9be21f1..5731ae4 100644 --- a/Backend/ERPCore/Services/BundleSaleService.cs +++ b/Backend/ERPCore/Services/BundleSaleService.cs @@ -178,7 +178,7 @@ public sealed class BundleSaleService : IBundleSaleService }; bundle.CustomerSnapshotName = await _customers.Query().Where(x => x.CustomerId == request.CustomerId).Select(x => x.DisplayName ?? x.Name).FirstAsync(ct); bundle.Lines = await BuildLinesAsync(template, request.WarehouseId, request.Lines, ct); - Recalculate(bundle, request.BundlePrice); + Recalculate(bundle, request.BundlePrice, request.CardAmount); bundle.BundleCode = $"{bundle.BundleNo}-B"; await _bundles.AddAsync(bundle, ct); await _uow.SaveChangesAsync(ct); @@ -203,7 +203,7 @@ public sealed class BundleSaleService : IBundleSaleService bundle.CustomerSnapshotName = await _customers.Query().Where(x => x.CustomerId == request.CustomerId).Select(x => x.DisplayName ?? x.Name).FirstAsync(ct); bundle.Lines.Clear(); foreach (var line in await BuildLinesAsync(template, request.WarehouseId, request.Lines, ct)) bundle.Lines.Add(line); - Recalculate(bundle, request.BundlePrice); + Recalculate(bundle, request.BundlePrice, request.CardAmount); bundle.UpdatedAt = DateTime.UtcNow; await _uow.SaveChangesAsync(ct); return Map(bundle); @@ -273,7 +273,8 @@ public sealed class BundleSaleService : IBundleSaleService return lines; } - private static void Recalculate(BundleSale bundle, decimal bundlePrice) + /// A bundle sale is always fully paid; null means all cash. + private static void Recalculate(BundleSale bundle, decimal bundlePrice, decimal? cardAmount) { bundle.ComponentSubtotal = bundle.Lines.Where(x => x.IncludeInBundle).Sum(x => x.LineTotal); bundle.BundlePrice = bundlePrice; @@ -281,6 +282,13 @@ public sealed class BundleSaleService : IBundleSaleService bundle.DiscountTotal = Math.Max(0m, bundle.ComponentSubtotal - bundle.BundlePrice); bundle.TaxTotal = 0m; bundle.GrandTotal = bundle.BundlePrice + bundle.TaxTotal; + + var card = cardAmount ?? 0m; + if (card < 0 || card > bundle.GrandTotal) + throw new DomainException(ErrorCodes.SalesCardAmountInvalid, + $"cardAmount must be between 0 and the bundle's total {bundle.GrandTotal}.", 400); + bundle.CardAmount = card; + bundle.CashAmount = bundle.GrandTotal - card; } private static BundleSaleSummaryDto MapSummary(BundleSale x) => new( @@ -290,6 +298,7 @@ public sealed class BundleSaleService : IBundleSaleService x.BundleSaleId, x.BundleNo, x.BundleDate, x.CustomerId, x.CustomerSnapshotName, x.WarehouseId, x.CashierUserId, x.BundleSaleTemplateId, x.BundleName, x.BundleCode, x.Status, x.ComponentSubtotal, x.BundlePrice, x.MarginAmount, x.DiscountTotal, x.TaxTotal, x.GrandTotal, x.CreatedAt, x.UpdatedAt, - x.Lines.Select(l => new BundleSaleLineDto(l.BundleSaleLineId, l.ItemId, l.Description, l.Qty, l.WarehouseId, l.UnitPrice, l.LineTotal, l.IncludeInBundle, l.IsComponent, l.ParentLineId)).ToList()); + x.Lines.Select(l => new BundleSaleLineDto(l.BundleSaleLineId, l.ItemId, l.Description, l.Qty, l.WarehouseId, l.UnitPrice, l.LineTotal, l.IncludeInBundle, l.IsComponent, l.ParentLineId)).ToList(), + x.CashAmount, x.CardAmount); } diff --git a/Backend/ERPCore/Services/BusinessDayClock.cs b/Backend/ERPCore/Services/BusinessDayClock.cs new file mode 100644 index 0000000..d8dec1b --- /dev/null +++ b/Backend/ERPCore/Services/BusinessDayClock.cs @@ -0,0 +1,34 @@ +namespace ERPCore.Services; + +/// +/// The business calendar day shared by the Day-End Sales Report and the Bank Deposit module +/// (docs/BANK_DEPOSIT_MODULE_PLAN.md §7.1). Sales documents are stamped in UTC, so a business day +/// is the DayEndReport:TimeZone calendar day converted to a UTC [start, end) window. +/// One instance per app — the zone is resolved once from configuration. +/// +public sealed class BusinessDayClock +{ + public BusinessDayClock(IConfiguration configuration) + => Zone = ResolveZone(configuration["DayEndReport:TimeZone"]); + + public TimeZoneInfo Zone { get; } + + /// Today in the business time zone. + public DateOnly Today => LocalDay(DateTime.UtcNow); + + /// The UTC instant the local day starts at. + public DateTime ToUtc(DateOnly localDay) + => TimeZoneInfo.ConvertTimeToUtc(localDay.ToDateTime(TimeOnly.MinValue, DateTimeKind.Unspecified), Zone); + + /// The local business day a UTC timestamp falls on. + public DateOnly LocalDay(DateTime utc) + => DateOnly.FromDateTime(TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(utc, DateTimeKind.Utc), Zone)); + + private static TimeZoneInfo ResolveZone(string? id) + { + if (string.IsNullOrWhiteSpace(id)) return TimeZoneInfo.Utc; + try { return TimeZoneInfo.FindSystemTimeZoneById(id); } + catch (TimeZoneNotFoundException) { return TimeZoneInfo.Utc; } + catch (InvalidTimeZoneException) { return TimeZoneInfo.Utc; } + } +} diff --git a/Backend/ERPCore/Services/CashInHandService.cs b/Backend/ERPCore/Services/CashInHandService.cs new file mode 100644 index 0000000..98698ee --- /dev/null +++ b/Backend/ERPCore/Services/CashInHandService.cs @@ -0,0 +1,196 @@ +using System.Globalization; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Accounts; +using ERPCore.Infra.Auth; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Till cash in hand (docs/BANK_DEPOSIT_MODULE_PLAN.md §2, §7.1). Only cash counts: slip and bundle +/// CashAmount and cash invoice payments come in, cash expense payments go out, and Posted bank +/// deposits take it to the bank. The balance carries from day to day starting at +/// BankDeposit:CashTrackingStartDate; the opening balance before a window is a handful of +/// database sums, and the days inside it are bucketed by local day here (same reason as +/// : MariaDB can't portably group by a zone-shifted date). +/// +public sealed class CashInHandService : ICashInHandService +{ + private const int MaxRangeDays = 92; + + private readonly IRepository _slips; + private readonly IRepository _bundles; + private readonly IRepository _invoicePayments; + private readonly IRepository _expensePayments; + private readonly IRepository _deposits; + private readonly ICurrentBranchContext _currentBranch; + private readonly BusinessDayClock _clock; + private readonly string? _defaultCashAccountCode; + + public CashInHandService( + IRepository slips, IRepository bundles, IRepository invoicePayments, + IRepository expensePayments, IRepository deposits, + ICurrentBranchContext currentBranch, BusinessDayClock clock, IConfiguration configuration) + { + _slips = slips; + _bundles = bundles; + _invoicePayments = invoicePayments; + _expensePayments = expensePayments; + _deposits = deposits; + _currentBranch = currentBranch; + _clock = clock; + _defaultCashAccountCode = configuration["BankDeposit:DefaultCashAccountCode"]; + TrackingStartDate = DateOnly.TryParse(configuration["BankDeposit:CashTrackingStartDate"], CultureInfo.InvariantCulture, out var start) + ? start + : null; + } + + public DateOnly? TrackingStartDate { get; } + + public async Task GetPositionAsync(DateOnly? day, int? branchId, CancellationToken ct = default) + { + var date = day ?? _clock.Today; + var branch = EffectiveBranch(branchId); + var today = _clock.Today; + + // One pass from the day to today serves both the day's own figures and what is still depositable. + var days = await ComputeAsync(date, date > today ? date : today, branch, ct); + return new CashInHandDto(days[0], Available(days, date, today), branch, TrackingStartDate, _clock.Zone.Id, _defaultCashAccountCode); + } + + public async Task GetDailyAsync(DateOnly from, DateOnly to, int? branchId, CancellationToken ct = default) + { + if (to < from) + throw new DomainException(ErrorCodes.DayEndReportRangeInvalid, "'to' must be on or after 'from'.", 400); + if (to.DayNumber - from.DayNumber + 1 > MaxRangeDays) + throw new DomainException(ErrorCodes.DayEndReportRangeInvalid, $"The range can be at most {MaxRangeDays} days.", 400); + + var branch = EffectiveBranch(branchId); + var days = await ComputeAsync(from, to, branch, ct); + return new CashInHandDailyDto(from, to, days, branch, TrackingStartDate, _clock.Zone.Id); + } + + public async Task> GetRangeAsync(DateOnly from, DateOnly to, int? branchId, CancellationToken ct = default) + => await ComputeAsync(from, to, EffectiveBranch(branchId), ct); + + public async Task GetAvailableToDepositAsync(DateOnly day, int? branchScope, CancellationToken ct = default) + { + var today = _clock.Today; + if (day > today || !IsTracked(day)) return 0m; + return Available(await ComputeAsync(day, today, branchScope, ct), day, today); + } + + /// A branch-scoped (non-admin) caller always sees only their own branch, whatever they ask for. + private int? EffectiveBranch(int? requested) => _currentBranch.IsActive ? _currentBranch.BranchId : requested; + + private bool IsTracked(DateOnly day) => TrackingStartDate is not { } start || day >= start; + + /// Lowest closing balance from to today: depositing more would overdraw a later day. + private decimal Available(IReadOnlyList days, DateOnly day, DateOnly today) + { + if (day > today || !IsTracked(day)) return 0m; + var min = days.Where(d => d.Date >= day && d.Date <= today).Min(d => d.Closing); + return Math.Max(0m, min); + } + + private async Task> ComputeAsync(DateOnly from, DateOnly to, int? branch, CancellationToken ct) + { + // Opening: everything tracked before the window — [start, from). + var opening = 0m; + if (TrackingStartDate is not { } trackStart || trackStart < from) + { + DateTime? lo = TrackingStartDate is { } s ? _clock.ToUtc(s) : null; + var hi = _clock.ToUtc(from); + var cashIn = await Slips(lo, hi, branch).SumAsync(s => s.CashAmount, ct) + + await Bundles(lo, hi, branch).SumAsync(b => b.CashAmount, ct) + + await InvoicePayments(lo, hi, branch).SumAsync(p => p.Amount, ct); + var cashOut = await ExpensePayments(lo, hi, branch).SumAsync(p => p.Amount, ct); + var deposited = await Deposits(TrackingStartDate, from, branch).SumAsync(d => d.Amount, ct); + opening = cashIn - cashOut - deposited; + } + + // The window itself, bucketed by local day. + var start = _clock.ToUtc(from); + var end = _clock.ToUtc(to.AddDays(1)); + var inRows = new List<(DateTime At, decimal Amount)>(); + inRows.AddRange((await Slips(start, end, branch).Select(s => new { s.SlipDate, s.CashAmount }).ToListAsync(ct)) + .Select(r => (r.SlipDate, r.CashAmount))); + inRows.AddRange((await Bundles(start, end, branch).Select(b => new { b.BundleDate, b.CashAmount }).ToListAsync(ct)) + .Select(r => (r.BundleDate, r.CashAmount))); + inRows.AddRange((await InvoicePayments(start, end, branch).Select(p => new { p.PaymentDate, p.Amount }).ToListAsync(ct)) + .Select(r => (r.PaymentDate, r.Amount))); + var outRows = await ExpensePayments(start, end, branch).Select(p => new { p.PaymentDate, p.Amount }).ToListAsync(ct); + var depositRows = await Deposits(from, to.AddDays(1), branch).Select(d => new { d.BusinessDate, d.Amount }).ToListAsync(ct); + + var inByDay = inRows.GroupBy(r => _clock.LocalDay(r.At)).ToDictionary(g => g.Key, g => g.Sum(r => r.Amount)); + var outByDay = outRows.GroupBy(r => _clock.LocalDay(r.PaymentDate)).ToDictionary(g => g.Key, g => g.Sum(r => r.Amount)); + var depositByDay = depositRows.GroupBy(r => r.BusinessDate).ToDictionary(g => g.Key, g => g.Sum(r => r.Amount)); + + var days = new List(to.DayNumber - from.DayNumber + 1); + var running = opening; + for (var day = from; day <= to; day = day.AddDays(1)) + { + var cashIn = inByDay.GetValueOrDefault(day); + var cashOut = outByDay.GetValueOrDefault(day); + var deposited = depositByDay.GetValueOrDefault(day); + var net = cashIn - cashOut; + + // Before tracking starts nothing carries in or out; the day stands alone. + var tracked = IsTracked(day); + var open = tracked ? running : 0m; + var close = open + net - deposited; + if (tracked) running = close; + + days.Add(new CashPositionDto(day, open, cashIn, cashOut, net, deposited, close)); + } + return days; + } + + // --- Sources. lo is inclusive (null = no lower bound), hi exclusive. --- + + private IQueryable Slips(DateTime? lo, DateTime hi, int? branch) + { + var q = _slips.Query().AsNoTracking() + .Where(s => s.Status == SalesSlipStatus.Posted && s.CashAmount != 0 && s.SlipDate < hi); + if (lo is not null) q = q.Where(s => s.SlipDate >= lo); + return branch is null ? q : q.Where(s => s.Warehouse!.BranchId == branch); + } + + private IQueryable Bundles(DateTime? lo, DateTime hi, int? branch) + { + var q = _bundles.Query().AsNoTracking() + .Where(b => b.Status == BundleSaleStatus.Posted && b.CashAmount != 0 && b.BundleDate < hi); + if (lo is not null) q = q.Where(b => b.BundleDate >= lo); + return branch is null ? q : q.Where(b => b.Warehouse!.BranchId == branch); + } + + /// Cash received against Posted invoices — whatever day the invoice itself is from. + private IQueryable InvoicePayments(DateTime? lo, DateTime hi, int? branch) + { + var q = _invoicePayments.Query().AsNoTracking() + .Where(p => p.PaymentMethod == SalesPaymentMethod.Cash && p.SalesInvoice!.Status == SalesInvoiceStatus.Posted && p.PaymentDate < hi); + if (lo is not null) q = q.Where(p => p.PaymentDate >= lo); + return branch is null ? q : q.Where(p => p.SalesInvoice!.BranchId == branch); + } + + private IQueryable ExpensePayments(DateTime? lo, DateTime hi, int? branch) + { + var q = _expensePayments.Query().AsNoTracking() + .Where(p => p.PaymentMethod == ExpensePaymentMethod.Cash && p.Expense!.Status != ExpenseStatus.Cancelled && p.PaymentDate < hi); + if (lo is not null) q = q.Where(p => p.PaymentDate >= lo); + return branch is null ? q : q.Where(p => p.Expense!.BranchId == branch); + } + + /// Posted deposits by business date; inclusive (null = no bound), exclusive. + private IQueryable Deposits(DateOnly? lo, DateOnly hi, int? branch) + { + var q = _deposits.Query().AsNoTracking() + .Where(d => d.Status == BankDepositStatus.Posted && d.BusinessDate < hi); + if (lo is not null) q = q.Where(d => d.BusinessDate >= lo); + return branch is null ? q : q.Where(d => d.BranchId == branch); + } +} diff --git a/Backend/ERPCore/Services/CountService.cs b/Backend/ERPCore/Services/CountService.cs index 1011121..cd02c5b 100644 --- a/Backend/ERPCore/Services/CountService.cs +++ b/Backend/ERPCore/Services/CountService.cs @@ -16,53 +16,47 @@ namespace ERPCore.Services; /// /// Stock-count service (FR-STK-08). Create snapshots system quantities (immutable, -/// 02-SECURITY C.7); posting emits a variance via the -/// shared (a variance is an adjustment in disguise, C.7) -/// and closes the count — all in one UoW transaction. GL posting for that variance -/// mirrors 's (same account config, same gain/loss -/// split) since it's the exact same kind of document under the hood. +/// 02-SECURITY C.7); posting closes the count and hands its variances to +/// as one adjustment (a variance is an adjustment in disguise, +/// C.7). That adjustment follows the same approval rules as any other +/// (docs/DUAL_APPROVAL_PLAN.md §8, decision 3) — a count is not a way around approval — and +/// moves stock when it is approved, or at once when approvals are switched off. /// public sealed class CountService : ICountService { private const string VarianceReasonCode = "VAR"; private readonly IRepository _counts; - private readonly IRepository _adjustments; private readonly IRepository _warehouses; private readonly IRepository _items; private readonly IRepository _reasonCodes; private readonly IFifoCostingService _fifo; - private readonly IStockMutator _mutator; private readonly INumberSequenceService _numbers; private readonly ICurrentUser _currentUser; private readonly IBranchConfigService _branchConfig; private readonly IUnitOfWork _uow; - private readonly IGeneralLedgerService _gl; private readonly IRepository _stockLayers; private readonly IRepository _batches; private readonly IRepository _serials; private readonly IGlAccountConfigService _glAccounts; public CountService( - IRepository counts, IRepository adjustments, IRepository warehouses, - IRepository items, IRepository reasonCodes, IFifoCostingService fifo, IStockMutator mutator, + IRepository counts, IRepository warehouses, + IRepository items, IRepository reasonCodes, IFifoCostingService fifo, INumberSequenceService numbers, ICurrentUser currentUser, IBranchConfigService branchConfig, IUnitOfWork uow, IGeneralLedgerService gl, IRepository stockLayers, IRepository batches, IRepository serials, IGlAccountConfigService glAccounts) { _serialTracking = serialTracking; _counts = counts; - _adjustments = adjustments; _warehouses = warehouses; _items = items; _reasonCodes = reasonCodes; _fifo = fifo; - _mutator = mutator; _numbers = numbers; _currentUser = currentUser; _branchConfig = branchConfig; _uow = uow; - _gl = gl; _stockLayers = stockLayers; _batches = batches; _serials = serials; @@ -266,13 +260,9 @@ public sealed class CountService : ICountService // Batch and serial lines adjust exactly their own lot/unit. var varianceLines = count.Lines.Where(l => l.Variance.HasValue && l.Variance.Value != 0).ToList(); - var deltas = varianceLines - .Select(l => new StockDelta(l.ItemId, l.BinId, l.BatchId, l.Variance!.Value, - l.SerialId is int serialId ? new List { serialId } : null)) - .ToList(); // No variances → just close the count, no adjustment. - if (deltas.Count == 0) + if (varianceLines.Count == 0) { count.Status = CountStatus.Posted; await _uow.SaveChangesAsync(ct); @@ -283,53 +273,33 @@ public sealed class CountService : ICountService .FirstOrDefaultAsync(r => r.Context == ReasonContext.Adjustment && r.Code == VarianceReasonCode, ct) ?? throw new DomainException(ErrorCodes.Validation, $"Reason code '{VarianceReasonCode}' (Count Variance) is not configured.", 422); - var now = DateTime.UtcNow; - var (adjustmentId, glJournalNo, ledgerEntries) = await _uow.ExecuteInTransactionAsync(async token => + // A serial counted missing is written off (Scrapped); one found is brought back InStock — + // the adjustment's own serial rules, applied when it posts. + var request = new CreateAdjustmentRequest { - var docNo = await _numbers.NextAsync(DocumentTypes.Adjustment, token); - var adjustment = new StockAdjustment + WarehouseId = count.WarehouseId, + BranchId = count.BranchId, + ReasonCodeId = reason.ReasonCodeId, + Lines = varianceLines.Select(l => new CreateAdjustmentLineInput { - DocNo = docNo, - WarehouseId = count.WarehouseId, - BranchId = count.BranchId, - ReasonCodeId = reason.ReasonCodeId, - Status = AdjustmentStatus.Posted, - CreatedBy = _currentUser.AuditUserId, - CreatedAt = now, - Lines = deltas.Select(d => new StockAdjustmentLine - { - ItemId = d.ItemId, - BinId = d.BinId, - BatchId = d.BatchId, - SerialId = d.SerialIds?.FirstOrDefault(), - QtyDelta = d.QtyDelta - }).ToList() - }; - await _adjustments.AddAsync(adjustment, token); - await _uow.SaveChangesAsync(token); // flush for a valid ledger sourceDocId - - var refs = await _mutator.ApplyAsync(count.WarehouseId, DocumentTypes.Adjustment, adjustment.AdjustmentId, now, deltas, token); - - // A serial counted missing is written off (Scrapped); one found keeps/regains InStock here. - foreach (var line in varianceLines.Where(l => l.SerialId is not null)) - { - var serial = await _serials.Query().FirstAsync(s => s.SerialId == line.SerialId, token); - var found = line.Variance > 0; - await _serialTracking.MoveAsync(new[] { serial }, - found ? SerialStatus.InStock : SerialStatus.Scrapped, - found ? count.WarehouseId : null, found ? line.BinId : null, - DocumentTypes.Count, count.CountId, now, token); - } - - adjustment.GlJournalNo = await PostAdjustmentJournalAsync(docNo, now, refs, token); - if (adjustment.GlJournalNo is not null) adjustment.GlPostedAt = now; + ItemId = l.ItemId, + BinId = l.BinId, + BatchId = l.BatchId, + QtyDelta = l.Variance!.Value, + SerialIds = l.SerialId is int missing && l.Variance < 0 ? [missing] : null, + SerialNos = l.Serial is not null && l.Variance > 0 ? [l.Serial.SerialNo] : null + }).ToList() + }; + var (adjustment, refs) = await _adjustments.SubmitAsync(request, _ => + { count.Status = CountStatus.Posted; - return (adjustment.AdjustmentId, adjustment.GlJournalNo, refs); + return Task.CompletedTask; }, ct); - // Map ledger ids after commit so they are populated. - return new CountPostResultDto(count.CountId, count.Status, adjustmentId, glJournalNo, ledgerEntries.Select(r => r.LedgerId).ToList()); + return new CountPostResultDto( + count.CountId, count.Status, adjustment.AdjustmentId, adjustment.GlJournalNo, refs.Select(r => r.LedgerId).ToList(), + adjustment.Status, adjustment.DocNo); } private static CountDto Map(StockCount c) => new( diff --git a/Backend/ERPCore/Services/CustomerService.cs b/Backend/ERPCore/Services/CustomerService.cs index fc7b112..35931c9 100644 --- a/Backend/ERPCore/Services/CustomerService.cs +++ b/Backend/ERPCore/Services/CustomerService.cs @@ -17,6 +17,8 @@ public sealed class CustomerService : ICustomerService private readonly IRepository _warehouses; private readonly IBranchConfigService _branchConfig; private readonly IRepository _salesConfig; + private readonly IRepository _invoices; + private readonly IRepository _invoicePayments; private readonly IUnitOfWork _uow; /// Code of the system "Cash Customer" row (see ). @@ -24,12 +26,15 @@ public sealed class CustomerService : ICustomerService public CustomerService( IRepository customers, IRepository warehouses, IBranchConfigService branchConfig, - IRepository salesConfig, IUnitOfWork uow) + IRepository salesConfig, IRepository invoices, + IRepository invoicePayments, IUnitOfWork uow) { _customers = customers; _warehouses = warehouses; _branchConfig = branchConfig; _salesConfig = salesConfig; + _invoices = invoices; + _invoicePayments = invoicePayments; _uow = uow; } @@ -70,7 +75,40 @@ public sealed class CustomerService : ICustomerService c.CreditLimit, c.CreditDays, c.DefaultWarehouseId, c.Status, c.BranchId, c.CreatedAt, c.UpdatedAt)) .ToListAsync(ct); - return PagedResponse.Create(rows, query.Page, query.PageSize, total); + return PagedResponse.Create(await WithReceivablesAsync(rows, ct), query.Page, query.PageSize, total); + } + + /// + /// Adds each customer's receivable position — the unpaid balance and count of their Posted sales + /// invoices, and their latest invoice payment — for just the customers on this page. + /// + private async Task> WithReceivablesAsync(List rows, CancellationToken ct) + { + if (rows.Count == 0) return rows; + var ids = rows.Select(r => r.CustomerId).ToList(); + + var dues = await _invoices.Query().AsNoTracking() + .Where(i => ids.Contains(i.CustomerId) && i.Status == SalesInvoiceStatus.Posted && i.BalanceAmount > 0) + .GroupBy(i => i.CustomerId) + .Select(g => new { CustomerId = g.Key, Amount = g.Sum(i => i.BalanceAmount), Count = g.Count() }) + .ToDictionaryAsync(x => x.CustomerId, ct); + + var lastPaid = await _invoicePayments.Query().AsNoTracking() + .Where(p => ids.Contains(p.SalesInvoice!.CustomerId)) + .GroupBy(p => p.SalesInvoice!.CustomerId) + .Select(g => new { CustomerId = g.Key, Last = g.Max(p => p.PaymentDate) }) + .ToDictionaryAsync(x => x.CustomerId, x => x.Last, ct); + + return rows.Select(r => + { + var due = dues.GetValueOrDefault(r.CustomerId); + return r with + { + OutstandingAmount = due?.Amount ?? 0m, + OutstandingInvoiceCount = due?.Count ?? 0, + LastPaymentDate = lastPaid.TryGetValue(r.CustomerId, out var last) ? last : null + }; + }).ToList(); } public async Task?> GetAsync(int customerId, CancellationToken ct = default) diff --git a/Backend/ERPCore/Services/DayEndReportService.cs b/Backend/ERPCore/Services/DayEndReportService.cs new file mode 100644 index 0000000..89a8e8c --- /dev/null +++ b/Backend/ERPCore/Services/DayEndReportService.cs @@ -0,0 +1,284 @@ +using ERPCore.Domain; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Accounts; +using ERPCore.Dtos.Reports; +using ERPCore.Infra.Auth; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Day-End Sales Report (docs/DAY_END_SALES_REPORT_PLAN.md §4). Sales documents are stamped in +/// UTC, so each business day is the calendar day converted to a UTC +/// [start, end) window. The database does the filtering and returns slim projections; the +/// rows are bucketed into local days here, since MariaDB can't group by a time-zone-shifted date +/// portably and a ≤ 92-day window of slim rows is small. Cost of goods sold is the stock ledger's +/// Out value per sale document — the same source posts COGS from. +/// The cash figures (net cash, deposits, cash in hand) come from the Bank Deposit module's +/// (docs/BANK_DEPOSIT_MODULE_PLAN.md §9); this report owns none of them. +/// +public sealed class DayEndReportService : IDayEndReportService +{ + private const int MaxRangeDays = 92; + private const int IdChunk = 1000; + + private readonly IRepository _slips; + private readonly IRepository _bundles; + private readonly IRepository _invoices; + private readonly IRepository _invoicePayments; + private readonly IRepository _ledger; + private readonly IRepository _expenses; + private readonly IRepository _deposits; + private readonly ICashInHandService _cash; + private readonly ICurrentBranchContext _currentBranch; + private readonly BusinessDayClock _clock; + + public DayEndReportService( + IRepository slips, IRepository bundles, IRepository invoices, + IRepository invoicePayments, IRepository ledger, + IRepository expenses, IRepository deposits, ICashInHandService cash, + ICurrentBranchContext currentBranch, BusinessDayClock clock) + { + _slips = slips; + _bundles = bundles; + _invoices = invoices; + _invoicePayments = invoicePayments; + _ledger = ledger; + _expenses = expenses; + _deposits = deposits; + _cash = cash; + _currentBranch = currentBranch; + _clock = clock; + } + + public async Task GetDayAsync(DateOnly? requestedDate, int? branchId, CancellationToken ct = default) + { + var date = requestedDate ?? _clock.Today; + var branch = EffectiveBranch(branchId); + var data = await LoadAsync(date, date, branch, ct); + + var sales = data.Sales + .OrderBy(s => s.At) + .Select(s => new DayEndReportSaleDto( + s.DocType, s.DocId, s.DocNo, s.At, s.Customer, s.Total, s.Paid, s.Total - s.Paid, s.PaidBy, s.Cost, s.Total - s.Cost)) + .ToList(); + + var expenses = await PostedExpenses(date, date, branch) + .OrderBy(e => e.ExpenseId) + .Select(e => new DayEndReportExpenseDto( + e.ExpenseId, e.DocNo, e.Category!.Name, e.Title!.Name, e.Payee, e.Amount, e.PaidAmount)) + .ToListAsync(ct); + + var depositsQuery = _deposits.Query().AsNoTracking() + .Where(d => d.Status == BankDepositStatus.Posted && d.BusinessDate == date); + if (branch is not null) depositsQuery = depositsQuery.Where(d => d.BranchId == branch); + var deposits = await depositsQuery + .OrderBy(d => d.BankDepositId) + .Select(d => new DayEndReportDepositDto( + d.BankDepositId, d.DocNo, d.ToBankAccountName, d.ToBankName, d.ToAccountNumber, d.DepositSlipNo, d.DepositDate, d.Amount)) + .ToListAsync(ct); + + return new DayEndReportDto(data.Days[0], sales, expenses, deposits, branch, _clock.Zone.Id); + } + + public async Task GetDailyAsync(DateOnly from, DateOnly to, int? branchId, CancellationToken ct = default) + { + if (to < from) + throw new DomainException(ErrorCodes.DayEndReportRangeInvalid, "'to' must be on or after 'from'.", 400); + if (to.DayNumber - from.DayNumber + 1 > MaxRangeDays) + throw new DomainException(ErrorCodes.DayEndReportRangeInvalid, $"The range can be at most {MaxRangeDays} days.", 400); + + var branch = EffectiveBranch(branchId); + var data = await LoadAsync(from, to, branch, ct); + return new DayEndDailyReportDto(from, to, data.Days, Sum(data.Days), branch, _clock.Zone.Id); + } + + /// A branch-scoped (non-admin) caller always sees only their own branch, whatever they ask for. + private int? EffectiveBranch(int? requested) => _currentBranch.IsActive ? _currentBranch.BranchId : requested; + + /// One sale, whatever document it is. Cash/Card/Other are how its Paid amount was paid. + private sealed record Sale( + string DocType, int DocId, string DocNo, DateTime At, DateOnly Day, string Customer, + decimal Total, decimal Paid, decimal Cash, decimal Card, decimal Other, string PaidBy, decimal Cost); + + private sealed record Loaded(List Days, List Sales); + + private async Task LoadAsync(DateOnly from, DateOnly to, int? branch, CancellationToken ct) + { + var start = _clock.ToUtc(from); + var end = _clock.ToUtc(to.AddDays(1)); + + var slipsQuery = _slips.Query().AsNoTracking() + .Where(s => s.Status == SalesSlipStatus.Posted && s.SlipDate >= start && s.SlipDate < end); + if (branch is not null) slipsQuery = slipsQuery.Where(s => s.Warehouse!.BranchId == branch); + var slips = await slipsQuery + .Select(s => new { s.SalesSlipId, s.SlipNo, s.SlipDate, s.CustomerSnapshotName, s.GrandTotal, s.PaidAmount, s.CashAmount, s.CardAmount }) + .ToListAsync(ct); + + var bundlesQuery = _bundles.Query().AsNoTracking() + .Where(b => b.Status == BundleSaleStatus.Posted && b.BundleDate >= start && b.BundleDate < end); + if (branch is not null) bundlesQuery = bundlesQuery.Where(b => b.Warehouse!.BranchId == branch); + var bundles = await bundlesQuery + .Select(b => new { b.BundleSaleId, b.BundleNo, b.BundleDate, b.CustomerSnapshotName, b.GrandTotal, b.CashAmount, b.CardAmount }) + .ToListAsync(ct); + + var invoicesQuery = _invoices.Query().AsNoTracking() + .Where(i => i.Status == SalesInvoiceStatus.Posted && i.InvoiceDate >= start && i.InvoiceDate < end); + if (branch is not null) invoicesQuery = invoicesQuery.Where(i => i.BranchId == branch); + var invoices = await invoicesQuery + .Select(i => new { i.SalesInvoiceId, i.InvoiceNo, i.InvoiceDate, i.CustomerSnapshotName, i.NetPayable, i.PaidAmount }) + .ToListAsync(ct); + + // Every payment that matters to the window: those against its invoices (the method split of + // Paid), and those received inside it against earlier invoices (collections / net cash). + var paymentsQuery = _invoicePayments.Query().AsNoTracking() + .Where(p => p.SalesInvoice!.Status == SalesInvoiceStatus.Posted + && ((p.SalesInvoice.InvoiceDate >= start && p.SalesInvoice.InvoiceDate < end) + || (p.PaymentDate >= start && p.PaymentDate < end))); + if (branch is not null) paymentsQuery = paymentsQuery.Where(p => p.SalesInvoice!.BranchId == branch); + var payments = await paymentsQuery + .Select(p => new { p.SalesInvoiceId, InvoiceDate = p.SalesInvoice!.InvoiceDate, p.PaymentDate, p.PaymentMethod, p.Amount }) + .ToListAsync(ct); + + var slipCost = await CostByDocAsync(DocumentTypes.SalesSlip, slips.Select(s => s.SalesSlipId), ct); + var bundleCost = await CostByDocAsync(DocumentTypes.BundleSale, bundles.Select(b => b.BundleSaleId), ct); + var invoiceCost = await CostByDocAsync(DocumentTypes.SalesInvoice, invoices.Select(i => i.SalesInvoiceId), ct); + + var expenses = await PostedExpenses(from, to, branch) + .Select(e => new { e.ExpenseDate, e.Amount }) + .ToListAsync(ct); + + // Branch is already the effective one, so the cash service scopes it the same way. + var cashByDay = (await _cash.GetRangeAsync(from, to, branch, ct)).ToDictionary(c => c.Date); + + var paymentsByInvoice = payments.GroupBy(p => p.SalesInvoiceId).ToDictionary(g => g.Key, g => g.ToList()); + + var sales = new List(slips.Count + bundles.Count + invoices.Count); + sales.AddRange(slips.Select(s => new Sale( + "Slip", s.SalesSlipId, s.SlipNo, s.SlipDate, LocalDay(s.SlipDate), s.CustomerSnapshotName, + s.GrandTotal, s.PaidAmount, s.CashAmount, s.CardAmount, 0m, PaidBy(s.CashAmount, s.CardAmount, 0m, null), + slipCost.GetValueOrDefault(s.SalesSlipId)))); + sales.AddRange(bundles.Select(b => new Sale( + "Bundle", b.BundleSaleId, b.BundleNo, b.BundleDate, LocalDay(b.BundleDate), b.CustomerSnapshotName, + b.GrandTotal, b.GrandTotal, b.CashAmount, b.CardAmount, 0m, PaidBy(b.CashAmount, b.CardAmount, 0m, null), + bundleCost.GetValueOrDefault(b.BundleSaleId)))); + foreach (var i in invoices) + { + var own = paymentsByInvoice.GetValueOrDefault(i.SalesInvoiceId) ?? []; + var cash = own.Where(p => p.PaymentMethod == SalesPaymentMethod.Cash).Sum(p => p.Amount); + var card = own.Where(p => p.PaymentMethod == SalesPaymentMethod.Card).Sum(p => p.Amount); + var others = own.Where(p => p.PaymentMethod is not (SalesPaymentMethod.Cash or SalesPaymentMethod.Card)).ToList(); + var other = others.Sum(p => p.Amount); + var otherMethods = others.Select(p => p.PaymentMethod).Distinct().ToList(); + sales.Add(new Sale( + "Invoice", i.SalesInvoiceId, i.InvoiceNo, i.InvoiceDate, LocalDay(i.InvoiceDate), i.CustomerSnapshotName, + i.NetPayable, i.PaidAmount, cash, card, other, + PaidBy(cash, card, other, otherMethods.Count == 1 ? otherMethods[0] : null), + invoiceCost.GetValueOrDefault(i.SalesInvoiceId))); + } + + var days = new List(to.DayNumber - from.DayNumber + 1); + for (var day = from; day <= to; day = day.AddDays(1)) + { + var d = day; + var acc = new DayAccumulator(); + foreach (var s in sales.Where(s => s.Day == d)) acc.AddSale(s); + + var receivedToday = payments.Where(p => LocalDay(p.PaymentDate) == d).ToList(); + acc.EarlierCollections = receivedToday.Where(p => LocalDay(p.InvoiceDate) < d).Sum(p => p.Amount); + acc.ExpenseTotal = expenses.Where(e => e.ExpenseDate == d).Sum(e => e.Amount); + acc.Cash = cashByDay[d]; + + days.Add(acc.ToDto(d)); + } + return new Loaded(days, sales); + } + + /// Sum of the stock ledger's Out value per document — the FIFO cost the sale consumed. + private async Task> CostByDocAsync(string docType, IEnumerable docIds, CancellationToken ct) + { + var result = new Dictionary(); + foreach (var chunk in docIds.Distinct().Chunk(IdChunk)) + { + var rows = await _ledger.Query().AsNoTracking() + .Where(l => l.SourceDocType == docType && l.Direction == Direction.Out && chunk.Contains(l.SourceDocId)) + .GroupBy(l => l.SourceDocId) + .Select(g => new { DocId = g.Key, Cost = g.Sum(l => l.Value) }) + .ToListAsync(ct); + foreach (var r in rows) result[r.DocId] = r.Cost; + } + return result; + } + + private static string PaidBy(decimal cash, decimal card, decimal other, SalesPaymentMethod? singleOther) + { + var ways = (cash > 0 ? 1 : 0) + (card > 0 ? 1 : 0) + (other > 0 ? 1 : 0); + if (ways == 0) return "Unpaid"; + if (ways > 1 || (other > 0 && singleOther is null)) return "Mixed"; + if (cash > 0) return "Cash"; + if (card > 0) return "Card"; + return singleOther == SalesPaymentMethod.Cheque ? "Cheque" : "Bank transfer"; + } + + private IQueryable PostedExpenses(DateOnly from, DateOnly to, int? branch) + { + var q = _expenses.Query().AsNoTracking() + .Where(e => e.Status == ExpenseStatus.Posted && e.ExpenseDate >= from && e.ExpenseDate <= to); + return branch is null ? q : q.Where(e => e.BranchId == branch); + } + + private DateOnly LocalDay(DateTime utc) => _clock.LocalDay(utc); + + /// Flows are summed; cash in hand is a balance, so the range opens with its first day and closes with its last. + private static DayEndReportDayDto Sum(IReadOnlyList days) => new( + null, + days.Sum(d => d.TotalSales), days.Sum(d => d.PaidAmount), days.Sum(d => d.DueAmount), + days.Sum(d => d.PaidByCash), days.Sum(d => d.PaidByCard), days.Sum(d => d.PaidByOther), + days.Sum(d => d.SalesCount), days.Sum(d => d.CashSalesCount), days.Sum(d => d.CardSalesCount), + days.Sum(d => d.MixedSalesCount), days.Sum(d => d.OtherSalesCount), days.Sum(d => d.UnpaidSalesCount), + days.Sum(d => d.ExpenseTotal), days.Sum(d => d.Income), + days.Sum(d => d.CostOfGoodsSold), days.Sum(d => d.GrossProfit), days.Sum(d => d.NetProfit), + days.Sum(d => d.EarlierInvoiceCollections), days.Sum(d => d.NetCash), + days[0].OpeningCashInHand, days.Sum(d => d.CashDeposited), days[^1].ClosingCashInHand); + + private sealed class DayAccumulator + { + private decimal _total, _paid, _cash, _card, _other, _cost; + private int _count, _cashCount, _cardCount, _mixedCount, _otherCount, _unpaidCount; + public decimal ExpenseTotal { get; set; } + public decimal EarlierCollections { get; set; } + public CashPositionDto? Cash { get; set; } + + /// A sale counts as a cash/card/other sale when that is the only way it was paid, + /// mixed when paid more than one way, unpaid when nothing has been paid yet. + public void AddSale(Sale s) + { + _total += s.Total; + _paid += s.Paid; + _cash += s.Cash; + _card += s.Card; + _other += s.Other; + _cost += s.Cost; + _count++; + + var ways = (s.Cash > 0 ? 1 : 0) + (s.Card > 0 ? 1 : 0) + (s.Other > 0 ? 1 : 0); + if (ways == 0) _unpaidCount++; + else if (ways > 1) _mixedCount++; + else if (s.Cash > 0) _cashCount++; + else if (s.Card > 0) _cardCount++; + else _otherCount++; + } + + public DayEndReportDayDto ToDto(DateOnly date) => new( + date, _total, _paid, _total - _paid, _cash, _card, _other, + _count, _cashCount, _cardCount, _mixedCount, _otherCount, _unpaidCount, + ExpenseTotal, _total - ExpenseTotal, + _cost, _total - _cost, _total - _cost - ExpenseTotal, + EarlierCollections, + Cash?.NetCash ?? 0m, Cash?.Opening ?? 0m, Cash?.Deposited ?? 0m, Cash?.Closing ?? 0m); + } +} diff --git a/Backend/ERPCore/Services/ExpensePaymentService.cs b/Backend/ERPCore/Services/ExpensePaymentService.cs new file mode 100644 index 0000000..ed2ad94 --- /dev/null +++ b/Backend/ERPCore/Services/ExpensePaymentService.cs @@ -0,0 +1,189 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Expenses; +using ERPCore.Infra.Auth; +using ERPCore.Infra.UoW; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Gl; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Payments against a Posted expense (docs/EXPENSE_MODULE_PLAN.md §4). One submission may +/// split across several cash/bank accounts; it posts a single GL journal entry (Debit Accrued +/// Expenses / Credit each account) and records one row per line, +/// all sharing that journal number. Same call-GL-before-commit pattern as +/// , so a rejected/unreachable GL post rolls the +/// whole payment back. Post & Pay runs the accrual and a full or partial payment in one transaction. +/// +public sealed class ExpensePaymentService : IExpensePaymentService +{ + private readonly IRepository _expenses; + private readonly IRepository _payments; + private readonly ExpenseService _expenseService; + private readonly IGeneralLedgerService _gl; + private readonly ICurrentUser _currentUser; + private readonly IUnitOfWork _uow; + private readonly string _glAccruedExpensesCode; + + public ExpensePaymentService( + IRepository expenses, IRepository payments, ExpenseService expenseService, + IGeneralLedgerService gl, ICurrentUser currentUser, IUnitOfWork uow, IConfiguration configuration) + { + _expenses = expenses; + _payments = payments; + _expenseService = expenseService; + _gl = gl; + _currentUser = currentUser; + _uow = uow; + _glAccruedExpensesCode = configuration["Expense:GlAccruedExpensesCode"] ?? string.Empty; + } + + public async Task> PayAsync(int expenseId, CreateExpensePaymentRequest request, CancellationToken ct = default) + { + var expense = await _expenseService.LoadAsync(expenseId, tracking: true, ct) + ?? throw new NotFoundException($"Expense {expenseId} was not found."); + + if (expense.Status != ExpenseStatus.Posted || expense.BalanceAmount <= 0) + throw new DomainException(ErrorCodes.ExpenseNotPayable, + $"Expense {expense.DocNo} must be posted with a balance outstanding before it can be paid.", 409); + + var total = ValidateRequest(request); + if (total > expense.BalanceAmount) + throw new DomainException(ErrorCodes.ExpensePaymentExceedsBalance, + $"Payment total {total} exceeds the remaining balance {expense.BalanceAmount}.", 400); + + var accounts = await ResolveAccountsAsync(request, ct); + var (paymentDay, paymentDate) = ResolvePaymentDate(request.PaymentDate, DateTime.UtcNow); + + var recorded = await _uow.ExecuteInTransactionAsync( + token => RecordPaymentAsync(expense, request, accounts, paymentDay, paymentDate, token), ct); + + return recorded.Select(ExpenseService.MapPayment).ToList(); + } + + public async Task PostAndPayAsync(int expenseId, CreateExpensePaymentRequest request, CancellationToken ct = default) + { + var expense = await _expenseService.LoadAsync(expenseId, tracking: true, ct) + ?? throw new NotFoundException($"Expense {expenseId} was not found."); + + if (expense.Status != ExpenseStatus.Draft) + throw new DomainException(ErrorCodes.ExpenseNotDraft, $"Expense {expense.DocNo} is {expense.Status}; use the payments endpoint instead.", 409); + + // Full or partial: whatever is left stays as the balance, payable later like any Posted expense. + var total = ValidateRequest(request); + if (total > expense.Amount) + throw new DomainException(ErrorCodes.ExpensePaymentExceedsBalance, + $"Payment total {total} exceeds the expense amount {expense.Amount}.", 400); + + var accounts = await ResolveAccountsAsync(request, ct); + var (paymentDay, paymentDate) = ResolvePaymentDate(request.PaymentDate, DateTime.UtcNow); + + await _uow.ExecuteInTransactionAsync(async token => + { + await _expenseService.PostInCurrentTransactionAsync(expense, token); + return await RecordPaymentAsync(expense, request, accounts, paymentDay, paymentDate, token); + }, ct); + + return ExpenseService.Map(expense); + } + + public async Task> ListAsync(int expenseId, CancellationToken ct = default) + { + if (!await _expenses.Query().AnyAsync(x => x.ExpenseId == expenseId, ct)) + throw new NotFoundException($"Expense {expenseId} was not found."); + + var payments = await _payments.Query().AsNoTracking() + .Where(p => p.ExpenseId == expenseId) + .OrderByDescending(p => p.ExpensePaymentId) + .ToListAsync(ct); + return payments.Select(ExpenseService.MapPayment).ToList(); + } + + /// Posts the settlement journal and records the payment rows. Runs inside the caller's transaction. + private async Task> RecordPaymentAsync( + Expense expense, CreateExpensePaymentRequest request, IReadOnlyDictionary accounts, + DateOnly paymentDay, DateTime paymentDate, CancellationToken ct) + { + var total = request.Lines.Sum(l => l.Amount); + var narration = $"Payment of expense {expense.DocNo}"; + var glLines = new List { new(_glAccruedExpensesCode, total, 0m, narration) }; + glLines.AddRange(request.Lines.Select(l => new GlJournalEntryLineRequest(accounts[l.GlBankAccountId].GlAccountCode, 0m, l.Amount, narration))); + + var period = await _gl.GetPeriodByDateAsync(paymentDay, ct); + var posted = await _gl.PostJournalEntryAsync(new GlJournalEntryRequest + { + PeriodId = period.PeriodId, + EntryDate = paymentDay, + SourceModule = "EXPENSE_PAYMENT", + Reference = expense.DocNo, + Narration = narration, + Lines = glLines + }, ct); + + var actor = _currentUser.AuditUserId; + var now = DateTime.UtcNow; + var chequeNo = request.PaymentMethod == ExpensePaymentMethod.Cheque ? request.ChequeNo?.Trim() : null; + var reference = string.IsNullOrWhiteSpace(request.Reference) ? null : request.Reference.Trim(); + + var rows = request.Lines.Select(l => new ExpensePayment + { + ExpenseId = expense.ExpenseId, + Amount = l.Amount, + PaymentDate = paymentDate, + PaymentMethod = request.PaymentMethod, + GlBankAccountId = l.GlBankAccountId, + BankAccountName = accounts[l.GlBankAccountId].AccountName, + ChequeNo = chequeNo, + Reference = reference, + GlJournalNo = posted.JournalNo, + CreatedBy = actor, + CreatedAt = now + }).ToList(); + + foreach (var row in rows) expense.Payments.Add(row); + expense.PaidAmount += total; + expense.BalanceAmount -= total; + expense.PaymentStatus = ExpenseService.DerivePaymentStatus(expense.PaidAmount, expense.BalanceAmount); + expense.UpdatedAt = now; + + return rows; + } + + private static decimal ValidateRequest(CreateExpensePaymentRequest request) + { + if (request.Lines.Count == 0) + throw new DomainException(ErrorCodes.Validation, "Add at least one payment line.", 400); + if (request.Lines.Any(l => l.Amount <= 0)) + throw new DomainException(ErrorCodes.Validation, "Every payment line needs an amount greater than zero.", 400); + if (request.PaymentMethod == ExpensePaymentMethod.Cheque && string.IsNullOrWhiteSpace(request.ChequeNo)) + throw new DomainException(ErrorCodes.ExpenseChequeNoRequired, "Enter the cheque number for a cheque payment.", 400); + return request.Lines.Sum(l => l.Amount); + } + + private async Task> ResolveAccountsAsync(CreateExpensePaymentRequest request, CancellationToken ct) + { + var all = await _gl.ListBankAccountsAsync(ct); + var result = new Dictionary(); + foreach (var id in request.Lines.Select(l => l.GlBankAccountId).Distinct()) + { + result[id] = all.FirstOrDefault(a => a.AccountId == id) + ?? throw new DomainException(ErrorCodes.ExpenseBankAccountNotFound, $"Bank/cash account {id} was not found.", 404); + } + return result; + } + + /// Same rule as : omitted ⇒ today, one day of + /// slack on "future" for clients east of UTC; a back-dated payment is stamped at that day's start. + private static (DateOnly Day, DateTime Stamp) ResolvePaymentDate(DateOnly? requested, DateTime now) + { + var today = DateOnly.FromDateTime(now); + var day = requested ?? today; + if (day > today.AddDays(1)) + throw new DomainException(ErrorCodes.Validation, "Payment date cannot be in the future.", 400); + return (day, day >= today ? now : day.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc)); + } +} diff --git a/Backend/ERPCore/Services/ExpenseService.cs b/Backend/ERPCore/Services/ExpenseService.cs new file mode 100644 index 0000000..9800c5d --- /dev/null +++ b/Backend/ERPCore/Services/ExpenseService.cs @@ -0,0 +1,390 @@ +using ERPCore.Common.Http; +using ERPCore.Domain; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Common; +using ERPCore.Dtos.Expenses; +using ERPCore.Infra.Auth; +using ERPCore.Infra.UoW; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Gl; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Business expense bills (docs/EXPENSE_MODULE_PLAN.md §4). Draft is editable and has no GL +/// impact; Post accrues it (Debit the title's expense account / Credit Accrued Expenses) via a +/// real GL journal entry posted before commit — same atomicity approach as +/// , so a GL rejection rolls the post back. +/// Payments settle the accrual (). Cancelling a Posted +/// expense with no payments posts the reversing entry. +/// +public sealed class ExpenseService : IExpenseService +{ + private readonly IRepository _expenses; + private readonly IRepository _titles; + private readonly IRepository _vendors; + private readonly ICurrentUser _currentUser; + private readonly INumberSequenceService _numbers; + private readonly IBranchConfigService _branchConfig; + private readonly IUnitOfWork _uow; + private readonly IGeneralLedgerService _gl; + private readonly string _glAccruedExpensesCode; + + public ExpenseService( + IRepository expenses, IRepository titles, IRepository vendors, + ICurrentUser currentUser, INumberSequenceService numbers, IBranchConfigService branchConfig, + IUnitOfWork uow, IGeneralLedgerService gl, IConfiguration configuration) + { + _expenses = expenses; + _titles = titles; + _vendors = vendors; + _currentUser = currentUser; + _numbers = numbers; + _branchConfig = branchConfig; + _uow = uow; + _gl = gl; + _glAccruedExpensesCode = configuration["Expense:GlAccruedExpensesCode"] ?? string.Empty; + } + + public async Task> ListAsync(PageQuery query, ExpenseListFilter filter, CancellationToken ct = default) + { + IQueryable q = _expenses.Query().AsNoTracking().Include(x => x.Category).Include(x => x.Title); + if (!string.IsNullOrWhiteSpace(query.Q)) + { + var term = query.Q.Trim(); + q = q.Where(x => EF.Functions.Like(x.DocNo, $"%{term}%") + || (x.Payee != null && EF.Functions.Like(x.Payee, $"%{term}%")) + || (x.BillReference != null && EF.Functions.Like(x.BillReference, $"%{term}%"))); + } + if (filter.Status is not null) q = q.Where(x => x.Status == filter.Status); + if (filter.PaymentStatus is not null) q = q.Where(x => x.Status == ExpenseStatus.Posted && x.PaymentStatus == filter.PaymentStatus); + if (filter.CategoryId is not null) q = q.Where(x => x.ExpenseCategoryId == filter.CategoryId); + if (filter.TitleId is not null) q = q.Where(x => x.ExpenseTitleId == filter.TitleId); + if (filter.From is not null) q = q.Where(x => x.ExpenseDate >= filter.From); + if (filter.To is not null) q = q.Where(x => x.ExpenseDate <= filter.To); + if (filter.OverdueOnly) + { + var today = DateOnly.FromDateTime(DateTime.UtcNow); + q = q.Where(x => x.Status == ExpenseStatus.Posted && x.BalanceAmount > 0 && x.DueDate != null && x.DueDate < today); + } + + var total = await q.CountAsync(ct); + var rows = await q.OrderByDescending(x => x.ExpenseDate).ThenByDescending(x => x.ExpenseId) + .Skip(query.Skip).Take(query.PageSize).ToListAsync(ct); + + return PagedResponse.Create(rows.Select(MapSummary).ToList(), query.Page, query.PageSize, total); + } + + public async Task?> GetAsync(int expenseId, CancellationToken ct = default) + { + var expense = await LoadAsync(expenseId, tracking: false, ct); + return expense is null ? null : new ETagged(Map(expense), expense.RowVersion); + } + + public async Task> CreateAsync(CreateExpenseRequest request, CancellationToken ct = default) + { + var title = await ValidateTitleAsync(request, ct); + var vendorName = await ValidateVendorAsync(request.VendorId, ct); + ValidateDates(request); + + var branchId = request.BranchId > 0 + ? await _branchConfig.ResolveBranchIdAsync(request.BranchId, ct) + : await _branchConfig.ResolveDefaultBranchIdAsync(ct); + + var expense = await _uow.ExecuteInTransactionAsync(async token => + { + var entity = new Expense + { + DocNo = await _numbers.NextAsync(DocumentTypes.Expense, branchId, token), + BranchId = branchId, + Status = ExpenseStatus.Draft, + PaymentStatus = ExpensePaymentStatus.Unpaid, + CreatedBy = _currentUser.AuditUserId, + CreatedAt = DateTime.UtcNow + }; + Apply(entity, request, vendorName); + await _expenses.AddAsync(entity, token); + return entity; + }, ct); + + expense.Category = title.Category; + expense.Title = title; + return new ETagged(Map(expense), expense.RowVersion); + } + + public async Task> UpdateAsync(int expenseId, UpdateExpenseRequest request, uint expectedRowVersion, CancellationToken ct = default) + { + var expense = await LoadAsync(expenseId, tracking: true, ct) + ?? throw new NotFoundException($"Expense {expenseId} was not found."); + + if (expense.RowVersion != expectedRowVersion) + throw new DomainException(ErrorCodes.ConcurrencyConflict, "The expense was modified by another request.", 412); + EnsureDraft(expense, "edited"); + + var title = await ValidateTitleAsync(request, ct); + var vendorName = await ValidateVendorAsync(request.VendorId, ct); + ValidateDates(request); + + if (request.BranchId > 0 && request.BranchId != expense.BranchId) + expense.BranchId = await _branchConfig.ResolveBranchIdAsync(request.BranchId, ct); + + Apply(expense, request, vendorName); + expense.Category = title.Category; + expense.Title = title; + expense.UpdatedAt = DateTime.UtcNow; + + await SaveAsync(ct); + return new ETagged(Map(expense), expense.RowVersion); + } + + public async Task DeleteAsync(int expenseId, CancellationToken ct = default) + { + var expense = await _expenses.Query().FirstOrDefaultAsync(x => x.ExpenseId == expenseId, ct) + ?? throw new NotFoundException($"Expense {expenseId} was not found."); + EnsureDraft(expense, "deleted"); + + _expenses.Remove(expense); + await _uow.SaveChangesAsync(ct); + } + + public async Task PostAsync(int expenseId, CancellationToken ct = default) + { + var expense = await LoadAsync(expenseId, tracking: true, ct) + ?? throw new NotFoundException($"Expense {expenseId} was not found."); + + await _uow.ExecuteInTransactionAsync(token => PostInCurrentTransactionAsync(expense, token), ct); + return Map(expense); + } + + /// + /// Posts the accrual entry and flips the expense to Posted. Must run inside the caller's + /// transaction (the caller commits) — shared with + /// so Post & Pay is one atomic unit. + /// + internal async Task PostInCurrentTransactionAsync(Expense expense, CancellationToken ct) + { + EnsureDraft(expense, "posted"); + var title = expense.Title ?? throw new InvalidOperationException("Expense title must be loaded before posting."); + if (title.Status != EntityStatus.Active) + throw new DomainException(ErrorCodes.ExpenseTitleInactive, $"Expense title '{title.Name}' is inactive — pick another title before posting.", 409); + + var now = DateTime.UtcNow; + var narration = $"{expense.Category?.Name} - {title.Name} ({expense.DocNo})"; + var period = await _gl.GetPeriodByDateAsync(expense.ExpenseDate, ct); + var posted = await _gl.PostJournalEntryAsync(new GlJournalEntryRequest + { + PeriodId = period.PeriodId, + EntryDate = expense.ExpenseDate, + SourceModule = "EXPENSE", + Reference = expense.DocNo, + Narration = narration, + Lines = + [ + new GlJournalEntryLineRequest(title.GlExpenseAccountCode, expense.Amount, 0m, narration), + new GlJournalEntryLineRequest(_glAccruedExpensesCode, 0m, expense.Amount, narration) + ] + }, ct); + + expense.Status = ExpenseStatus.Posted; + expense.GlExpenseAccountCode = title.GlExpenseAccountCode; + expense.GlJournalNo = posted.JournalNo; + expense.GlPostedAt = now; + expense.UpdatedAt = now; + return 0; + } + + public async Task CancelAsync(int expenseId, CancelExpenseRequest request, CancellationToken ct = default) + { + var expense = await LoadAsync(expenseId, tracking: true, ct) + ?? throw new NotFoundException($"Expense {expenseId} was not found."); + + if (expense.Status == ExpenseStatus.Cancelled) + throw new ConflictException($"Expense {expense.DocNo} is already cancelled."); + if (expense.PaidAmount > 0) + throw new DomainException(ErrorCodes.ExpenseHasPayments, $"Expense {expense.DocNo} has payments recorded and cannot be cancelled.", 409); + + var now = DateTime.UtcNow; + await _uow.ExecuteInTransactionAsync(async token => + { + // A Draft never reached GL, so there is nothing to reverse. + if (expense.Status == ExpenseStatus.Posted) + { + var today = DateOnly.FromDateTime(now); + var narration = $"Cancellation of expense {expense.DocNo}"; + var period = await _gl.GetPeriodByDateAsync(today, token); + var reversal = await _gl.PostJournalEntryAsync(new GlJournalEntryRequest + { + PeriodId = period.PeriodId, + EntryDate = today, + SourceModule = "EXPENSE", + Reference = expense.DocNo, + Narration = narration, + Lines = + [ + new GlJournalEntryLineRequest(_glAccruedExpensesCode, expense.Amount, 0m, narration), + new GlJournalEntryLineRequest(expense.GlExpenseAccountCode ?? string.Empty, 0m, expense.Amount, narration) + ] + }, token); + expense.GlReversalJournalNo = reversal.JournalNo; + } + + expense.Status = ExpenseStatus.Cancelled; + expense.CancelReason = request.Reason.Trim(); + expense.UpdatedAt = now; + return 0; + }, ct); + + return Map(expense); + } + + public async Task GetSummaryAsync(DateOnly? from, DateOnly? to, CancellationToken ct = default) + { + var today = DateOnly.FromDateTime(DateTime.UtcNow); + var start = from ?? new DateOnly(today.Year, today.Month, 1); + var end = to ?? start.AddMonths(1).AddDays(-1); + if (end < start) + throw new DomainException(ErrorCodes.Validation, "'to' must not be before 'from'.", 400); + + var posted = _expenses.Query().AsNoTracking().Where(x => x.Status == ExpenseStatus.Posted); + + var inRange = await posted + .Where(x => x.ExpenseDate >= start && x.ExpenseDate <= end) + .Select(x => new + { + x.ExpenseCategoryId, CategoryName = x.Category!.Name, CategorySort = x.Category.SortOrder, + x.ExpenseTitleId, TitleName = x.Title!.Name, x.Amount, x.PaidAmount, x.BalanceAmount + }) + .ToListAsync(ct); + + var outstanding = await posted.Where(x => x.BalanceAmount > 0) + .Select(x => new { x.BalanceAmount, x.DueDate }) + .ToListAsync(ct); + var overdue = outstanding.Where(x => x.DueDate != null && x.DueDate < today).ToList(); + var draftCount = await _expenses.Query().CountAsync(x => x.Status == ExpenseStatus.Draft, ct); + + var categories = inRange + .GroupBy(x => new { x.ExpenseCategoryId, x.CategoryName, x.CategorySort }) + .OrderBy(g => g.Key.CategorySort).ThenBy(g => g.Key.CategoryName) + .Select(g => new ExpenseCategoryTotalDto( + g.Key.ExpenseCategoryId, g.Key.CategoryName, g.Sum(x => x.Amount), g.Count(), + g.GroupBy(x => new { x.ExpenseTitleId, x.TitleName }) + .OrderByDescending(t => t.Sum(x => x.Amount)) + .Select(t => new ExpenseTitleTotalDto(t.Key.ExpenseTitleId, t.Key.TitleName, t.Sum(x => x.Amount), t.Count())) + .ToList())) + .ToList(); + + return new ExpenseSummaryReportDto( + start, end, + inRange.Sum(x => x.Amount), inRange.Sum(x => x.PaidAmount), inRange.Sum(x => x.BalanceAmount), inRange.Count, + outstanding.Sum(x => x.BalanceAmount), overdue.Sum(x => x.BalanceAmount), overdue.Count, draftCount, + categories); + } + + internal Task LoadAsync(int expenseId, bool tracking, CancellationToken ct) + { + var q = _expenses.Query() + .Include(x => x.Category) + .Include(x => x.Title) + .Include(x => x.Vendor) + .Include(x => x.Payments); + return (tracking ? q : q.AsNoTracking()).FirstOrDefaultAsync(x => x.ExpenseId == expenseId, ct); + } + + private async Task ValidateTitleAsync(ExpenseRequestBase request, CancellationToken ct) + { + // Tracked on purpose: UpdateAsync assigns this to the tracked expense's navigations, and + // identity resolution hands back the instance the expense already loaded. + var title = await _titles.Query().Include(t => t.Category) + .FirstOrDefaultAsync(t => t.ExpenseTitleId == request.ExpenseTitleId, ct) + ?? throw new DomainException(ErrorCodes.Validation, $"Expense title {request.ExpenseTitleId} does not exist.", 422); + + if (title.ExpenseCategoryId != request.ExpenseCategoryId) + throw new DomainException(ErrorCodes.ExpenseTitleCategoryMismatch, $"Title '{title.Name}' does not belong to the selected category.", 422); + if (title.Status != EntityStatus.Active || title.Category?.Status != EntityStatus.Active) + throw new DomainException(ErrorCodes.ExpenseTitleInactive, $"Expense title '{title.Name}' or its category is inactive.", 422); + + return title; + } + + private async Task ValidateVendorAsync(int? vendorId, CancellationToken ct) + { + if (vendorId is null) return null; + return await _vendors.Query().AsNoTracking().Where(v => v.VendorId == vendorId).Select(v => v.Name).FirstOrDefaultAsync(ct) + ?? throw new DomainException(ErrorCodes.Validation, $"Vendor {vendorId} does not exist.", 422); + } + + private static void ValidateDates(ExpenseRequestBase request) + { + // One day of slack, as with payment dates: the client sends its local date. + if (request.ExpenseDate > DateOnly.FromDateTime(DateTime.UtcNow).AddDays(1)) + throw new DomainException(ErrorCodes.Validation, "Expense date cannot be in the future.", 400); + if (request.DueDate is not null && request.DueDate < request.ExpenseDate) + throw new DomainException(ErrorCodes.Validation, "Due date cannot be before the expense date.", 400); + } + + private static void Apply(Expense expense, ExpenseRequestBase request, string? vendorName) + { + expense.ExpenseDate = request.ExpenseDate; + expense.DueDate = request.DueDate; + expense.ExpenseCategoryId = request.ExpenseCategoryId; + expense.ExpenseTitleId = request.ExpenseTitleId; + expense.VendorId = request.VendorId; + // A linked vendor names the payee unless one was typed explicitly. + expense.Payee = Normalize(request.Payee) ?? vendorName; + expense.BillReference = Normalize(request.BillReference); + expense.Description = Normalize(request.Description); + expense.PeriodMonth = Normalize(request.PeriodMonth); + expense.Amount = request.Amount; + expense.PaidAmount = 0m; + expense.BalanceAmount = request.Amount; + expense.PaymentStatus = ExpensePaymentStatus.Unpaid; + } + + private static void EnsureDraft(Expense expense, string verb) + { + if (expense.Status != ExpenseStatus.Draft) + throw new DomainException(ErrorCodes.ExpenseNotDraft, $"Expense {expense.DocNo} is {expense.Status} and cannot be {verb}.", 409); + } + + private async Task SaveAsync(CancellationToken ct) + { + try + { + await _uow.SaveChangesAsync(ct); + } + catch (DbUpdateConcurrencyException) + { + throw new DomainException(ErrorCodes.ConcurrencyConflict, "The expense was modified by another request.", 412); + } + } + + private static string? Normalize(string? value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim(); + + internal static ExpensePaymentStatus DerivePaymentStatus(decimal paidAmount, decimal balanceAmount) => + balanceAmount <= 0 ? ExpensePaymentStatus.Paid + : paidAmount > 0 ? ExpensePaymentStatus.PartiallyPaid + : ExpensePaymentStatus.Unpaid; + + private static ExpenseSummaryDto MapSummary(Expense x) => new( + x.ExpenseId, x.DocNo, x.BranchId, x.ExpenseDate, x.DueDate, + x.ExpenseCategoryId, x.Category?.Name ?? string.Empty, x.ExpenseTitleId, x.Title?.Name ?? string.Empty, + x.Payee, x.BillReference, x.PeriodMonth, x.Amount, x.PaidAmount, x.BalanceAmount, + x.Status, x.PaymentStatus, x.GlJournalNo, x.CreatedAt); + + internal static ExpenseDto Map(Expense x) => new( + x.ExpenseId, x.DocNo, x.BranchId, x.ExpenseDate, x.DueDate, + x.ExpenseCategoryId, x.Category?.Name ?? string.Empty, x.ExpenseTitleId, x.Title?.Name ?? string.Empty, + x.Payee, x.VendorId, x.Vendor?.Name, x.BillReference, x.Description, x.PeriodMonth, + x.Amount, x.PaidAmount, x.BalanceAmount, x.Status, x.PaymentStatus, + x.GlExpenseAccountCode, x.GlJournalNo, x.GlPostedAt, x.GlReversalJournalNo, x.CancelReason, + x.CreatedBy, x.CreatedAt, x.UpdatedAt, + x.Payments.OrderByDescending(p => p.ExpensePaymentId).Select(MapPayment).ToList()); + + internal static ExpensePaymentDto MapPayment(ExpensePayment p) => new( + p.ExpensePaymentId, p.ExpenseId, p.Amount, p.PaymentDate, p.PaymentMethod, + p.GlBankAccountId, p.BankAccountName, p.ChequeNo, p.Reference, p.GlJournalNo, p.CreatedAt); +} diff --git a/Backend/ERPCore/Services/ExpenseSetupService.cs b/Backend/ERPCore/Services/ExpenseSetupService.cs new file mode 100644 index 0000000..54f27d8 --- /dev/null +++ b/Backend/ERPCore/Services/ExpenseSetupService.cs @@ -0,0 +1,133 @@ +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Expenses; +using ERPCore.Infra.UoW; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Expense categories and titles. Categories are the fixed seeded four (no create — only their +/// active flag changes). Seeded rows (IsSystem) keep their names — they can be +/// deactivated, and a title's GL account code can be corrected — while user-added titles +/// are fully editable. Nothing is hard-deleted: expenses reference titles, so retiring one +/// means setting it Inactive (it stops being offered on new expenses). +/// +public sealed class ExpenseSetupService : IExpenseSetupService +{ + private readonly IRepository _categories; + private readonly IRepository _titles; + private readonly IUnitOfWork _uow; + + public ExpenseSetupService(IRepository categories, IRepository titles, IUnitOfWork uow) + { + _categories = categories; + _titles = titles; + _uow = uow; + } + + public async Task> ListAsync(bool includeInactive, CancellationToken ct = default) + { + var categories = await _categories.Query().AsNoTracking() + .Include(c => c.Titles) + .Where(c => includeInactive || c.Status == EntityStatus.Active) + .OrderBy(c => c.SortOrder).ThenBy(c => c.Name) + .ToListAsync(ct); + + return categories.Select(c => Map(c, includeInactive)).ToList(); + } + + public async Task UpdateCategoryAsync(int expenseCategoryId, UpdateExpenseCategoryRequest request, CancellationToken ct = default) + { + var category = await _categories.Query().Include(c => c.Titles) + .FirstOrDefaultAsync(c => c.ExpenseCategoryId == expenseCategoryId, ct) + ?? throw new NotFoundException($"Expense category {expenseCategoryId} was not found."); + + var name = request.Name.Trim(); + if (name != category.Name) + { + if (category.IsSystem) + throw new DomainException(ErrorCodes.ExpenseSystemRowReadOnly, $"'{category.Name}' is a built-in category and cannot be renamed.", 409); + if (await _categories.Query().AnyAsync(c => c.Name == name && c.ExpenseCategoryId != expenseCategoryId, ct)) + throw new DomainException(ErrorCodes.DuplicateExpenseCategory, $"An expense category named '{name}' already exists.", 409); + category.Name = name; + } + + category.Status = request.Status; + category.UpdatedAt = DateTime.UtcNow; + await _uow.SaveChangesAsync(ct); + + return Map(category, includeInactive: true); + } + + public async Task CreateTitleAsync(int expenseCategoryId, CreateExpenseTitleRequest request, CancellationToken ct = default) + { + if (!await _categories.Query().AnyAsync(c => c.ExpenseCategoryId == expenseCategoryId, ct)) + throw new NotFoundException($"Expense category {expenseCategoryId} was not found."); + + var name = request.Name.Trim(); + await EnsureTitleNameIsUniqueAsync(expenseCategoryId, name, excludingTitleId: null, ct); + + var nextSort = (await _titles.Query().Where(t => t.ExpenseCategoryId == expenseCategoryId) + .MaxAsync(t => (int?)t.SortOrder, ct) ?? 0) + 1; + var title = new ExpenseTitle + { + ExpenseCategoryId = expenseCategoryId, + Name = name, + GlExpenseAccountCode = request.GlExpenseAccountCode.Trim(), + IsSystem = false, + SortOrder = nextSort, + CreatedAt = DateTime.UtcNow + }; + await _titles.AddAsync(title, ct); + await _uow.SaveChangesAsync(ct); + + return Map(title); + } + + public async Task UpdateTitleAsync(int expenseTitleId, UpdateExpenseTitleRequest request, CancellationToken ct = default) + { + var title = await _titles.Query().FirstOrDefaultAsync(t => t.ExpenseTitleId == expenseTitleId, ct) + ?? throw new NotFoundException($"Expense title {expenseTitleId} was not found."); + + var name = request.Name.Trim(); + if (name != title.Name) + { + if (title.IsSystem) + throw new DomainException(ErrorCodes.ExpenseSystemRowReadOnly, $"'{title.Name}' is a built-in title and cannot be renamed.", 409); + await EnsureTitleNameIsUniqueAsync(title.ExpenseCategoryId, name, excludingTitleId: expenseTitleId, ct); + title.Name = name; + } + + // The GL code is editable on system rows too — the seeded codes are placeholders. + // Already-posted expenses keep the code they snapshotted at post time. + title.GlExpenseAccountCode = request.GlExpenseAccountCode.Trim(); + title.Status = request.Status; + title.UpdatedAt = DateTime.UtcNow; + await _uow.SaveChangesAsync(ct); + + return Map(title); + } + + private async Task EnsureTitleNameIsUniqueAsync(int expenseCategoryId, string name, int? excludingTitleId, CancellationToken ct) + { + var duplicate = await _titles.Query().AnyAsync(t => + t.ExpenseCategoryId == expenseCategoryId && t.Name == name + && (excludingTitleId == null || t.ExpenseTitleId != excludingTitleId), ct); + if (duplicate) + throw new DomainException(ErrorCodes.DuplicateExpenseTitle, $"A title named '{name}' already exists in this category.", 409); + } + + private static ExpenseCategoryDto Map(ExpenseCategory c, bool includeInactive) => new( + c.ExpenseCategoryId, c.Code, c.Name, c.IsSystem, c.Status, c.SortOrder, + c.Titles + .Where(t => includeInactive || t.Status == EntityStatus.Active) + .OrderBy(t => t.SortOrder).ThenBy(t => t.Name) + .Select(Map).ToList()); + + private static ExpenseTitleDto Map(ExpenseTitle t) => new( + t.ExpenseTitleId, t.ExpenseCategoryId, t.Name, t.GlExpenseAccountCode, t.IsSystem, t.Status, t.SortOrder); +} diff --git a/Backend/ERPCore/Services/Interfaces/IAdjustmentService.cs b/Backend/ERPCore/Services/Interfaces/IAdjustmentService.cs index 60db2bb..51693a0 100644 --- a/Backend/ERPCore/Services/Interfaces/IAdjustmentService.cs +++ b/Backend/ERPCore/Services/Interfaces/IAdjustmentService.cs @@ -1,15 +1,25 @@ +using ERPCore.Domain.Enums; using ERPCore.Dtos.Common; using ERPCore.Dtos.Stock; namespace ERPCore.Services.Interfaces; -/// Stock-adjustment business logic (docs/11 §5.5; FR-STK-07; 02-SECURITY C.5). +/// Stock-adjustment business logic (docs/11 §5.5; FR-STK-07; 02-SECURITY C.5; docs/DUAL_APPROVAL_PLAN.md §5). public interface IAdjustmentService { Task> ListAsync( - PageQuery query, int? warehouseId, int? branchId, int? reasonCodeId, CancellationToken ct = default); + PageQuery query, int? warehouseId, int? branchId, int? reasonCodeId, AdjustmentStatus? status = null, CancellationToken ct = default); Task GetAsync(int adjustmentId, CancellationToken ct = default); + /// Submits an adjustment request — it posts at once only when approvals are switched off. Task CreateAsync(CreateAdjustmentRequest request, CancellationToken ct = default); + + /// Gives the next approval; the last one posts the stock movement and GL. + Task ApproveAsync(int adjustmentId, string? comment, CancellationToken ct = default); + + Task RejectAsync(int adjustmentId, string reason, CancellationToken ct = default); + + /// The requester withdraws a request that is still pending. + Task CancelAsync(int adjustmentId, CancellationToken ct = default); } diff --git a/Backend/ERPCore/Services/Interfaces/IApprovalService.cs b/Backend/ERPCore/Services/Interfaces/IApprovalService.cs new file mode 100644 index 0000000..2901a43 --- /dev/null +++ b/Backend/ERPCore/Services/Interfaces/IApprovalService.cs @@ -0,0 +1,48 @@ +using ERPCore.Dtos.Approvals; + +namespace ERPCore.Services.Interfaces; + +/// Where a document stands in its approval, as the shared engine needs to see it. +/// The screen whose Approve action an approver needs (e.g. stock.adjustments). +/// The approval being given now: 1, or 2 after the first. +public sealed record ApprovalTarget( + string DocType, int DocId, string DocNo, string NavCode, int RequestedBy, int RequiredApprovals, int CurrentLevel, int? Level1ApprovedBy); + +public enum ApprovalOutcome +{ + AdvancedToLevel2, + FullyApproved, + Rejected +} + +/// +/// The shared maker–checker rules for stock and price adjustments (docs/DUAL_APPROVAL_PLAN.md §4): +/// the requester can't approve, the two approvers must differ, level 2 may be limited to roles. +/// The Approve permission itself is checked by the endpoint's policy. Decisions are added to the +/// caller's unit of work — the document service saves them with its own changes. +/// +public interface IApprovalService +{ + Task GetConfigAsync(CancellationToken ct = default); + Task UpdateConfigAsync(UpdateApprovalConfigRequest request, CancellationToken ct = default); + + /// Approvals a stock adjustment worth needs (0, 1 or 2). + Task RequiredForStockAdjustmentAsync(decimal absoluteValue, CancellationToken ct = default); + + /// Approvals a price change of needs (0, 1 or 2). + Task RequiredForPriceAdjustmentAsync(decimal largestChangePercent, CancellationToken ct = default); + + /// Validates the current user may decide, and records the decision (unsaved). + Task DecideAsync(ApprovalTarget target, bool approve, string? comment, CancellationToken ct = default); + + /// Null when the current user may decide; otherwise the reason they can't. + Task WhyCannotDecideAsync(ApprovalTarget target, CancellationToken ct = default); + + Task> HistoryAsync(string docType, int docId, CancellationToken ct = default); + + /// Active users who could give 's current approval — for notifications. + Task> ApproverIdsAsync(ApprovalTarget target, CancellationToken ct = default); + + /// Notifies whoever can give the target's next approval that it is waiting. + Task NotifyPendingAsync(ApprovalTarget target, string summary, CancellationToken ct = default); +} diff --git a/Backend/ERPCore/Services/Interfaces/IBankDepositServices.cs b/Backend/ERPCore/Services/Interfaces/IBankDepositServices.cs new file mode 100644 index 0000000..89f9975 --- /dev/null +++ b/Backend/ERPCore/Services/Interfaces/IBankDepositServices.cs @@ -0,0 +1,40 @@ +using ERPCore.Dtos.Accounts; +using ERPCore.Dtos.Common; + +namespace ERPCore.Services.Interfaces; + +/// +/// Till cash in hand per branch and business day (docs/BANK_DEPOSIT_MODULE_PLAN.md §7.1) — the one +/// calculation both the deposit form and the Day-End Sales Report read. A branch-scoped caller +/// always gets their own branch; branchId null means every branch together. +/// +public interface ICashInHandService +{ + /// The configured BankDeposit:CashTrackingStartDate, if any. + DateOnly? TrackingStartDate { get; } + + /// null ⇒ today in the business time zone. + Task GetPositionAsync(DateOnly? day, int? branchId, CancellationToken ct = default); + + /// Every day of from..to (at most 92 days), opening carried in from earlier days. + Task GetDailyAsync(DateOnly from, DateOnly to, int? branchId, CancellationToken ct = default); + + /// Same as without the range cap — for callers that validate their own range. + Task> GetRangeAsync(DateOnly from, DateOnly to, int? branchId, CancellationToken ct = default); + + /// + /// The most that can still be deposited against : the lowest closing balance + /// from that day to today, never below zero; zero for a future day or one before tracking starts. + /// is used as given (the caller has already resolved it). + /// + Task GetAvailableToDepositAsync(DateOnly day, int? branchScope, CancellationToken ct = default); +} + +/// Bank deposits of till cash — record (posts GL), cancel (reverses GL), list (docs/BANK_DEPOSIT_MODULE_PLAN.md §7.2). +public interface IBankDepositService +{ + Task ListAsync(PageQuery query, BankDepositListFilter filter, CancellationToken ct = default); + Task GetAsync(int bankDepositId, CancellationToken ct = default); + Task CreateAsync(CreateBankDepositRequest request, CancellationToken ct = default); + Task CancelAsync(int bankDepositId, CancelBankDepositRequest request, CancellationToken ct = default); +} diff --git a/Backend/ERPCore/Services/Interfaces/IDayEndReportService.cs b/Backend/ERPCore/Services/Interfaces/IDayEndReportService.cs new file mode 100644 index 0000000..e657340 --- /dev/null +++ b/Backend/ERPCore/Services/Interfaces/IDayEndReportService.cs @@ -0,0 +1,11 @@ +using ERPCore.Dtos.Reports; + +namespace ERPCore.Services.Interfaces; + +/// Read-only day-end sales/expense/income report (docs/DAY_END_SALES_REPORT_PLAN.md §4). +public interface IDayEndReportService +{ + /// null ⇒ today in the report's time zone. + Task GetDayAsync(DateOnly? date, int? branchId, CancellationToken ct = default); + Task GetDailyAsync(DateOnly from, DateOnly to, int? branchId, CancellationToken ct = default); +} diff --git a/Backend/ERPCore/Services/Interfaces/IExpenseServices.cs b/Backend/ERPCore/Services/Interfaces/IExpenseServices.cs new file mode 100644 index 0000000..dc4e8a1 --- /dev/null +++ b/Backend/ERPCore/Services/Interfaces/IExpenseServices.cs @@ -0,0 +1,35 @@ +using ERPCore.Common.Http; +using ERPCore.Dtos.Common; +using ERPCore.Dtos.Expenses; + +namespace ERPCore.Services.Interfaces; + +/// Expense categories and their titles (docs/EXPENSE_MODULE_PLAN.md §2.1). +public interface IExpenseSetupService +{ + Task> ListAsync(bool includeInactive, CancellationToken ct = default); + Task UpdateCategoryAsync(int expenseCategoryId, UpdateExpenseCategoryRequest request, CancellationToken ct = default); + Task CreateTitleAsync(int expenseCategoryId, CreateExpenseTitleRequest request, CancellationToken ct = default); + Task UpdateTitleAsync(int expenseTitleId, UpdateExpenseTitleRequest request, CancellationToken ct = default); +} + +/// Expense bills: draft CRUD, post (GL accrual), cancel and reporting (docs/EXPENSE_MODULE_PLAN.md §4). +public interface IExpenseService +{ + Task> ListAsync(PageQuery query, ExpenseListFilter filter, CancellationToken ct = default); + Task?> GetAsync(int expenseId, CancellationToken ct = default); + Task> CreateAsync(CreateExpenseRequest request, CancellationToken ct = default); + Task> UpdateAsync(int expenseId, UpdateExpenseRequest request, uint expectedRowVersion, CancellationToken ct = default); + Task DeleteAsync(int expenseId, CancellationToken ct = default); + Task PostAsync(int expenseId, CancellationToken ct = default); + Task CancelAsync(int expenseId, CancelExpenseRequest request, CancellationToken ct = default); + Task GetSummaryAsync(DateOnly? from, DateOnly? to, CancellationToken ct = default); +} + +/// Payments against a Posted expense's balance, and the one-step Post & Pay. +public interface IExpensePaymentService +{ + Task> PayAsync(int expenseId, CreateExpensePaymentRequest request, CancellationToken ct = default); + Task PostAndPayAsync(int expenseId, CreateExpensePaymentRequest request, CancellationToken ct = default); + Task> ListAsync(int expenseId, CancellationToken ct = default); +} diff --git a/Backend/ERPCore/Services/Interfaces/IItemService.cs b/Backend/ERPCore/Services/Interfaces/IItemService.cs index 48e01c7..1020570 100644 --- a/Backend/ERPCore/Services/Interfaces/IItemService.cs +++ b/Backend/ERPCore/Services/Interfaces/IItemService.cs @@ -34,5 +34,8 @@ public interface IItemService /// Barcode/RFID scan lookup — item barcode/SKU, or a batch/serial barcode/no /// (optionally scoped to first). Null when nothing matches. + /// The item's most recent posted GRN receipts (newest first) — what it was bought at. + Task> GetPurchasePricesAsync(int itemId, int take = 5, CancellationToken ct = default); + Task ScanAsync(string code, int? itemId, CancellationToken ct = default); } diff --git a/Backend/ERPCore/Services/Interfaces/INotificationService.cs b/Backend/ERPCore/Services/Interfaces/INotificationService.cs index 1b84875..7c0da2d 100644 --- a/Backend/ERPCore/Services/Interfaces/INotificationService.cs +++ b/Backend/ERPCore/Services/Interfaces/INotificationService.cs @@ -17,6 +17,19 @@ public interface INotificationService int? relatedEntityId = null, CancellationToken ct = default); + /// + /// Same as but delivered only to (active ones) — + /// e.g. the approvers of a request, or its requester. Same InApp-channel gate; no-op for an empty list. + /// + Task NotifyUsersAsync( + IEnumerable userIds, + string moduleKey, + string title, + string message, + string? relatedEntityType = null, + int? relatedEntityId = null, + CancellationToken ct = default); + Task GetFeedAsync(int userId, int take = 20, CancellationToken ct = default); Task MarkAllReadAsync(int userId, CancellationToken ct = default); diff --git a/Backend/ERPCore/Services/Interfaces/IPriceAdjustmentService.cs b/Backend/ERPCore/Services/Interfaces/IPriceAdjustmentService.cs new file mode 100644 index 0000000..0e7d18f --- /dev/null +++ b/Backend/ERPCore/Services/Interfaces/IPriceAdjustmentService.cs @@ -0,0 +1,25 @@ +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Common; +using ERPCore.Dtos.Products; + +namespace ERPCore.Services.Interfaces; + +/// Selling-price change requests with dual approval (docs/DUAL_APPROVAL_PLAN.md §6). +public interface IPriceAdjustmentService +{ + Task> ListAsync(PageQuery query, PriceAdjustmentStatus? status, int? itemId, CancellationToken ct = default); + Task GetAsync(int priceAdjustmentId, CancellationToken ct = default); + + /// Submits a request — applied at once only when price approvals are switched off. + Task CreateAsync(CreatePriceAdjustmentRequest request, CancellationToken ct = default); + + /// Gives the next approval; the last one writes the new prices to the items. + Task ApproveAsync(int priceAdjustmentId, string? comment, CancellationToken ct = default); + Task RejectAsync(int priceAdjustmentId, string reason, CancellationToken ct = default); + Task CancelAsync(int priceAdjustmentId, CancellationToken ct = default); + + Task> ItemHistoryAsync(int itemId, CancellationToken ct = default); + + /// True when selling prices may only change through an approved price adjustment. + Task PricesNeedApprovalAsync(CancellationToken ct = default); +} diff --git a/Backend/ERPCore/Services/ItemService.cs b/Backend/ERPCore/Services/ItemService.cs index 944d6e6..1058f7c 100644 --- a/Backend/ERPCore/Services/ItemService.cs +++ b/Backend/ERPCore/Services/ItemService.cs @@ -32,6 +32,12 @@ public sealed class ItemService : IItemService private readonly IRepository _brands; private readonly IRepository _uoms; private readonly IRepository _taxRates; + private readonly IRepository _approvalConfig; + + /// Price approvals on (the default) unless Settings → Approvals set price levels to 0. + private async Task PricesNeedApprovalAsync(CancellationToken ct) + => (await _approvalConfig.Query().AsNoTracking().Where(c => c.ApprovalConfigId == ApprovalConfig.SingletonId) + .Select(c => (int?)c.PriceAdjustmentLevels).FirstOrDefaultAsync(ct) ?? 2) > 0; private readonly IRepository _vendors; private readonly IRepository _warehouses; private readonly IRepository _stockLayers; @@ -59,8 +65,10 @@ public sealed class ItemService : IItemService IRepository serials, IProductConfigService config, INotificationService notifications, - IUnitOfWork uow) + IUnitOfWork uow, + IRepository approvalConfig) { + _approvalConfig = approvalConfig; _items = items; _categories = categories; _subCategories = subCategories; @@ -207,6 +215,21 @@ public sealed class ItemService : IItemService return PagedResponse.Create(rows, query.Page, query.PageSize, total); } + public async Task> GetPurchasePricesAsync(int itemId, int take = 5, CancellationToken ct = default) + { + if (!await _items.Query().AnyAsync(i => i.ItemId == itemId, ct)) + throw new NotFoundException($"Item {itemId} was not found."); + + return await _grnLines.Query().AsNoTracking() + .Where(l => l.ItemId == itemId && l.Grn!.PostedAt != null) + .OrderByDescending(l => l.Grn!.PostedAt).ThenByDescending(l => l.GrnLineId) + .Take(Math.Clamp(take, 1, 50)) + .Select(l => new ItemPurchasePriceDto( + l.GrnId, l.Grn!.DocNo, l.Grn.PostedAt!.Value, l.Grn.VendorId, l.Grn.Vendor!.Name, l.Grn.Warehouse!.Code, + l.Qty, l.UnitCost, l.DiscountPct, l.NetUnitCost, l.VatPct)) + .ToListAsync(ct); + } + public async Task?> GetAsync(int itemId, CancellationToken ct = default) { var item = await _items.Query().AsNoTracking() @@ -309,6 +332,15 @@ public sealed class ItemService : IItemService ErrorCodes.MasterInUse, $"Item {itemId} has stock history; its base UOM can no longer be changed.", 409); + // With price approvals on, selling prices change only through an approved price adjustment + // (docs/DUAL_APPROVAL_PLAN.md §6.3). Sending the current prices back unchanged is fine. + var pricesChanged = item.SellingMinPrice != request.SellingMinPrice + || item.SellingMaxPrice != request.SellingMaxPrice + || item.SellingFlatPrice != request.SellingFlatPrice; + if (pricesChanged && await PricesNeedApprovalAsync(ct)) + throw new DomainException(ErrorCodes.PriceChangeRequiresApproval, + "Selling prices need approval — raise a price adjustment (Products → Price Adjustments) instead of editing them here.", 409); + ItemContent.ValidatePair(request.ContentQty, request.ContentUnit); var (contentBaseQty, contentBaseUnit) = ItemContent.Normalize(request.ContentQty, request.ContentUnit); var warrantyPeriodMonths = ValidateWarrantyPeriod( diff --git a/Backend/ERPCore/Services/NotificationService.cs b/Backend/ERPCore/Services/NotificationService.cs index 70708c9..cafb41a 100644 --- a/Backend/ERPCore/Services/NotificationService.cs +++ b/Backend/ERPCore/Services/NotificationService.cs @@ -39,16 +39,43 @@ public sealed class NotificationService : INotificationService int? relatedEntityId = null, CancellationToken ct = default) { - var channels = await _config.ResolveEnabledChannelsAsync(moduleKey, ct); - if (!channels.Contains(NotificationChannel.InApp)) return; - var activeUserIds = await _users.Query().AsNoTracking() .Where(u => u.Status == EntityStatus.Active) .Select(u => u.UserId) .ToListAsync(ct); + await DeliverAsync(activeUserIds, moduleKey, title, message, relatedEntityType, relatedEntityId, ct); + } + + public async Task NotifyUsersAsync( + IEnumerable userIds, + string moduleKey, + string title, + string message, + string? relatedEntityType = null, + int? relatedEntityId = null, + CancellationToken ct = default) + { + var wanted = userIds.Distinct().ToList(); + if (wanted.Count == 0) return; + + var activeUserIds = await _users.Query().AsNoTracking() + .Where(u => u.Status == EntityStatus.Active && wanted.Contains(u.UserId)) + .Select(u => u.UserId) + .ToListAsync(ct); + + await DeliverAsync(activeUserIds, moduleKey, title, message, relatedEntityType, relatedEntityId, ct); + } + + private async Task DeliverAsync( + List activeUserIds, string moduleKey, string title, string message, + string? relatedEntityType, int? relatedEntityId, CancellationToken ct) + { if (activeUserIds.Count == 0) return; + var channels = await _config.ResolveEnabledChannelsAsync(moduleKey, ct); + if (!channels.Contains(NotificationChannel.InApp)) return; + var notification = new Notification { ModuleKey = moduleKey, diff --git a/Backend/ERPCore/Services/PriceAdjustmentService.cs b/Backend/ERPCore/Services/PriceAdjustmentService.cs new file mode 100644 index 0000000..1984221 --- /dev/null +++ b/Backend/ERPCore/Services/PriceAdjustmentService.cs @@ -0,0 +1,364 @@ +using ERPCore.Domain; +using ERPCore.Domain.Entities; +using ERPCore.Domain.Enums; +using ERPCore.Dtos.Common; +using ERPCore.Dtos.Products; +using ERPCore.Infra.Auth; +using ERPCore.Infra.UoW; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Interfaces; +using ERPCore.System.Errors; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services; + +/// +/// Selling-price change requests (docs/DUAL_APPROVAL_PLAN.md §6). A request snapshots each item's +/// current prices and the prices wanted; nothing changes until it is fully approved (rules in +/// ). Approval then writes the new prices to the items and records an +/// row — unless an item was repriced since the request was made, in +/// which case the approval is refused as stale. One pending request per item at a time. +/// +public sealed class PriceAdjustmentService : IPriceAdjustmentService +{ + public const string ApprovalDocType = "PADJ"; + public const string ApprovalNavCode = "products.price-adjustments"; + + private readonly IRepository _adjustments; + private readonly IRepository _items; + private readonly IRepository _history; + private readonly IRepository _users; + private readonly IRepository _config; + private readonly IApprovalService _approvals; + private readonly INotificationService _notifications; + private readonly INumberSequenceService _numbers; + private readonly ICurrentUser _currentUser; + private readonly IUnitOfWork _uow; + + public PriceAdjustmentService( + IRepository adjustments, IRepository items, IRepository history, + IRepository users, IRepository config, IApprovalService approvals, + INotificationService notifications, INumberSequenceService numbers, ICurrentUser currentUser, IUnitOfWork uow) + { + _adjustments = adjustments; + _items = items; + _history = history; + _users = users; + _config = config; + _approvals = approvals; + _notifications = notifications; + _numbers = numbers; + _currentUser = currentUser; + _uow = uow; + } + + public async Task> ListAsync(PageQuery query, PriceAdjustmentStatus? status, int? itemId, CancellationToken ct = default) + { + var q = _adjustments.Query().AsNoTracking(); + if (!string.IsNullOrWhiteSpace(query.Q)) + { + var term = query.Q.Trim(); + q = q.Where(p => EF.Functions.Like(p.DocNo, $"%{term}%") || EF.Functions.Like(p.Reason, $"%{term}%")); + } + if (status is not null) q = q.Where(p => p.Status == status); + if (itemId is not null) q = q.Where(p => p.Lines.Any(l => l.ItemId == itemId)); + + var total = await q.CountAsync(ct); + var rows = await q.OrderByDescending(p => p.PriceAdjustmentId) + .Skip(query.Skip).Take(query.PageSize) + .Select(p => new PriceAdjustmentSummaryDto( + p.PriceAdjustmentId, p.DocNo, p.Reason, p.Status, p.RequiredApprovals, + p.Status == PriceAdjustmentStatus.PendingApproval ? 1 : p.Status == PriceAdjustmentStatus.PendingSecondApproval ? 2 : null, + p.Lines.Count, p.CreatedBy, p.Creator!.DisplayName, p.CreatedAt, p.AppliedAt)) + .ToListAsync(ct); + return PagedResponse.Create(rows, query.Page, query.PageSize, total); + } + + public async Task GetAsync(int priceAdjustmentId, CancellationToken ct = default) + { + var adjustment = await Load(tracking: false).FirstOrDefaultAsync(p => p.PriceAdjustmentId == priceAdjustmentId, ct); + return adjustment is null ? null : await ToDtoAsync(adjustment, ct); + } + + public async Task CreateAsync(CreatePriceAdjustmentRequest request, CancellationToken ct = default) + { + var reason = request.Reason?.Trim(); + if (string.IsNullOrEmpty(reason)) + throw new DomainException(ErrorCodes.Validation, "Give a reason for the price change.", 400); + if (request.Lines.Count == 0) + throw new DomainException(ErrorCodes.Validation, "Add at least one item.", 400); + if (request.Lines.Select(l => l.ItemId).Distinct().Count() != request.Lines.Count) + throw new DomainException(ErrorCodes.Validation, "Each item can appear only once in a request.", 400); + + var ids = request.Lines.Select(l => l.ItemId).ToList(); + var items = await _items.Query().Where(i => ids.Contains(i.ItemId)).ToDictionaryAsync(i => i.ItemId, ct); + var pendingItemIds = await _adjustments.Query().AsNoTracking() + .Where(p => p.Status == PriceAdjustmentStatus.PendingApproval || p.Status == PriceAdjustmentStatus.PendingSecondApproval) + .SelectMany(p => p.Lines.Select(l => new { l.ItemId, p.DocNo })) + .Where(x => ids.Contains(x.ItemId)) + .ToListAsync(ct); + + var lines = new List(); + var largestChange = 0m; + foreach (var input in request.Lines) + { + if (!items.TryGetValue(input.ItemId, out var item)) + throw new DomainException(ErrorCodes.Validation, $"Item {input.ItemId} does not exist.", 422); + var clash = pendingItemIds.FirstOrDefault(x => x.ItemId == input.ItemId); + if (clash is not null) + throw new DomainException(ErrorCodes.PriceAdjustmentItemPending, + $"{item.Sku} already has a pending price change ({clash.DocNo}) — approve, reject or withdraw that first.", 409); + ValidatePrices(item.Sku, input.NewMinPrice, input.NewMaxPrice, input.NewFlatPrice); + if (input.NewMinPrice == item.SellingMinPrice && input.NewMaxPrice == item.SellingMaxPrice && input.NewFlatPrice == item.SellingFlatPrice) + throw new DomainException(ErrorCodes.Validation, $"{item.Sku}: the new prices are the same as the current ones.", 400); + + largestChange = Math.Max(largestChange, Math.Max( + ChangePercent(item.SellingMinPrice, input.NewMinPrice), + Math.Max(ChangePercent(item.SellingMaxPrice, input.NewMaxPrice), ChangePercent(item.SellingFlatPrice, input.NewFlatPrice)))); + + lines.Add(new PriceAdjustmentLine + { + ItemId = item.ItemId, + OldMinPrice = item.SellingMinPrice, OldMaxPrice = item.SellingMaxPrice, OldFlatPrice = item.SellingFlatPrice, + NewMinPrice = input.NewMinPrice, NewMaxPrice = input.NewMaxPrice, NewFlatPrice = input.NewFlatPrice + }); + } + + var required = await _approvals.RequiredForPriceAdjustmentAsync(largestChange, ct); + var now = DateTime.UtcNow; + var adjustment = await _uow.ExecuteInTransactionAsync(async token => + { + var entity = new PriceAdjustment + { + DocNo = await _numbers.NextAsync(DocumentTypes.PriceAdjustment, token), + Reason = reason, + Status = required == 0 ? PriceAdjustmentStatus.Approved : PriceAdjustmentStatus.PendingApproval, + RequiredApprovals = required, + CreatedBy = _currentUser.AuditUserId, + CreatedAt = now, + Lines = lines + }; + await _adjustments.AddAsync(entity, token); + if (required == 0) + await ApplyInCurrentTransactionAsync(entity, items, now, token); + return entity; + }, ct); + + if (required > 0) + await _approvals.NotifyPendingAsync(Target(adjustment), Summary(adjustment), ct); + + return (await GetAsync(adjustment.PriceAdjustmentId, ct))!; + } + + public async Task ApproveAsync(int priceAdjustmentId, string? comment, CancellationToken ct = default) + { + var adjustment = await LoadPendingAsync(priceAdjustmentId, ct); + var target = Target(adjustment); + var outcome = await _approvals.DecideAsync(target, approve: true, comment, ct); + var now = DateTime.UtcNow; + + if (target.CurrentLevel == 1) + { + adjustment.ApprovedLevel1By = _currentUser.AuditUserId; + adjustment.ApprovedLevel1At = now; + } + else + { + adjustment.ApprovedLevel2By = _currentUser.AuditUserId; + adjustment.ApprovedLevel2At = now; + } + + try + { + await _uow.ExecuteInTransactionAsync(async token => + { + if (outcome == ApprovalOutcome.AdvancedToLevel2) + { + adjustment.Status = PriceAdjustmentStatus.PendingSecondApproval; + return 0; + } + var ids = adjustment.Lines.Select(l => l.ItemId).ToList(); + var items = await _items.Query().Where(i => ids.Contains(i.ItemId)).ToDictionaryAsync(i => i.ItemId, token); + adjustment.Status = PriceAdjustmentStatus.Approved; + await ApplyInCurrentTransactionAsync(adjustment, items, now, token); + return 0; + }, ct); + } + catch (DbUpdateConcurrencyException) + { + throw new DomainException(ErrorCodes.ApprovalNotPending, $"Price adjustment {adjustment.DocNo} was just decided by someone else — reload it.", 409); + } + + if (outcome == ApprovalOutcome.AdvancedToLevel2) + await _approvals.NotifyPendingAsync(Target(adjustment), Summary(adjustment), ct); + else + await _notifications.NotifyUsersAsync([adjustment.CreatedBy], "approval.approved", + $"Approved: {adjustment.DocNo}", $"Price adjustment {adjustment.DocNo} was approved — the new prices are now in effect.", + ApprovalDocType, adjustment.PriceAdjustmentId, ct); + + return (await GetAsync(adjustment.PriceAdjustmentId, ct))!; + } + + public async Task RejectAsync(int priceAdjustmentId, string reason, CancellationToken ct = default) + { + var adjustment = await LoadPendingAsync(priceAdjustmentId, ct); + await _approvals.DecideAsync(Target(adjustment), approve: false, reason, ct); + + adjustment.Status = PriceAdjustmentStatus.Rejected; + adjustment.RejectedBy = _currentUser.AuditUserId; + adjustment.RejectedAt = DateTime.UtcNow; + adjustment.RejectReason = reason.Trim(); + await SaveDecisionAsync(adjustment, ct); + + await _notifications.NotifyUsersAsync([adjustment.CreatedBy], "approval.rejected", + $"Rejected: {adjustment.DocNo}", $"Price adjustment {adjustment.DocNo} was rejected: {adjustment.RejectReason}", + ApprovalDocType, adjustment.PriceAdjustmentId, ct); + return (await GetAsync(adjustment.PriceAdjustmentId, ct))!; + } + + public async Task CancelAsync(int priceAdjustmentId, CancellationToken ct = default) + { + var adjustment = await LoadPendingAsync(priceAdjustmentId, ct); + if (adjustment.CreatedBy != _currentUser.AuditUserId) + throw new DomainException(ErrorCodes.ApprovalForbidden, "Only the person who made the request can withdraw it.", 403); + + adjustment.Status = PriceAdjustmentStatus.Cancelled; + adjustment.CancelledAt = DateTime.UtcNow; + await SaveDecisionAsync(adjustment, ct); + return (await GetAsync(adjustment.PriceAdjustmentId, ct))!; + } + + public async Task> ItemHistoryAsync(int itemId, CancellationToken ct = default) + { + var rows = await _history.Query().AsNoTracking() + .Where(h => h.ItemId == itemId) + .OrderByDescending(h => h.AppliedAt).ThenByDescending(h => h.ItemPriceHistoryId) + .ToListAsync(ct); + + var userIds = rows.SelectMany(r => new int?[] { r.RequestedBy, r.ApprovedLevel1By, r.ApprovedLevel2By }).OfType().Distinct().ToList(); + var names = await _users.Query().AsNoTracking().Where(u => userIds.Contains(u.UserId)).ToDictionaryAsync(u => u.UserId, u => u.DisplayName, ct); + var docIds = rows.Select(r => r.PriceAdjustmentId).OfType().Distinct().ToList(); + var docNos = await _adjustments.Query().AsNoTracking().Where(p => docIds.Contains(p.PriceAdjustmentId)).ToDictionaryAsync(p => p.PriceAdjustmentId, p => p.DocNo, ct); + + string? Name(int? id) => id is int v ? names.GetValueOrDefault(v) : null; + return rows.Select(h => new ItemPriceHistoryDto( + h.ItemPriceHistoryId, h.ItemId, h.PriceAdjustmentId, h.PriceAdjustmentId is int d ? docNos.GetValueOrDefault(d) : null, + h.OldMinPrice, h.OldMaxPrice, h.OldFlatPrice, h.NewMinPrice, h.NewMaxPrice, h.NewFlatPrice, + h.RequestedBy, Name(h.RequestedBy), h.ApprovedLevel1By, Name(h.ApprovedLevel1By), h.ApprovedLevel2By, Name(h.ApprovedLevel2By), + h.AppliedAt)).ToList(); + } + + public async Task PricesNeedApprovalAsync(CancellationToken ct = default) + => (await _config.Query().AsNoTracking().Where(c => c.ApprovalConfigId == ApprovalConfig.SingletonId) + .Select(c => (int?)c.PriceAdjustmentLevels).FirstOrDefaultAsync(ct) ?? 2) > 0; + + // --- Helpers --------------------------------------------------------------------------------- + + /// Writes each line's new prices to its item and records the history. Refuses the whole + /// request if any item's prices no longer match what the request was made against. + private async Task ApplyInCurrentTransactionAsync(PriceAdjustment adjustment, IReadOnlyDictionary items, DateTime now, CancellationToken ct) + { + foreach (var line in adjustment.Lines) + { + var item = items[line.ItemId]; + if (item.SellingMinPrice != line.OldMinPrice || item.SellingMaxPrice != line.OldMaxPrice || item.SellingFlatPrice != line.OldFlatPrice) + throw new DomainException(ErrorCodes.PriceAdjustmentStale, + $"{item.Sku}'s prices changed after {adjustment.DocNo} was requested. Reject this request and raise a new one against the current prices.", 409); + + item.SellingMinPrice = line.NewMinPrice; + item.SellingMaxPrice = line.NewMaxPrice; + item.SellingFlatPrice = line.NewFlatPrice; + item.UpdatedAt = now; + + await _history.AddAsync(new ItemPriceHistory + { + ItemId = item.ItemId, + PriceAdjustment = adjustment, // FK set by EF, also when the request is new + OldMinPrice = line.OldMinPrice, OldMaxPrice = line.OldMaxPrice, OldFlatPrice = line.OldFlatPrice, + NewMinPrice = line.NewMinPrice, NewMaxPrice = line.NewMaxPrice, NewFlatPrice = line.NewFlatPrice, + RequestedBy = adjustment.CreatedBy, + ApprovedLevel1By = adjustment.ApprovedLevel1By, + ApprovedLevel2By = adjustment.ApprovedLevel2By, + AppliedAt = now + }, ct); + } + adjustment.AppliedAt = now; + } + + private static void ValidatePrices(string sku, decimal? min, decimal? max, decimal? flat) + { + if (min < 0 || max < 0 || flat < 0) + throw new DomainException(ErrorCodes.Validation, $"{sku}: prices cannot be negative.", 400); + if (min is not null && max is not null && min > max) + throw new DomainException(ErrorCodes.Validation, $"{sku}: the minimum price is above the maximum.", 400); + if (flat is not null && ((min is not null && flat < min) || (max is not null && flat > max))) + throw new DomainException(ErrorCodes.Validation, $"{sku}: the flat price must be between the minimum and maximum.", 400); + } + + /// Size of a change in percent; setting or clearing a price counts as 100%. + public static decimal ChangePercent(decimal? oldPrice, decimal? newPrice) + { + if (oldPrice == newPrice) return 0m; + if (oldPrice is null || newPrice is null || oldPrice == 0) return 100m; + return Math.Abs(newPrice.Value - oldPrice.Value) / oldPrice.Value * 100m; + } + + private IQueryable Load(bool tracking) + { + var q = _adjustments.Query().Include(p => p.Lines).ThenInclude(l => l.Item).Include(p => p.Creator); + return tracking ? q : q.AsNoTracking(); + } + + private async Task LoadPendingAsync(int priceAdjustmentId, CancellationToken ct) + { + var adjustment = await _adjustments.Query().Include(p => p.Lines) + .FirstOrDefaultAsync(p => p.PriceAdjustmentId == priceAdjustmentId, ct) + ?? throw new NotFoundException($"Price adjustment {priceAdjustmentId} was not found."); + if (adjustment.Status is not (PriceAdjustmentStatus.PendingApproval or PriceAdjustmentStatus.PendingSecondApproval)) + throw new DomainException(ErrorCodes.ApprovalNotPending, $"Price adjustment {adjustment.DocNo} is {adjustment.Status} and not waiting for approval.", 409); + return adjustment; + } + + private async Task SaveDecisionAsync(PriceAdjustment adjustment, CancellationToken ct) + { + try + { + await _uow.SaveChangesAsync(ct); + } + catch (DbUpdateConcurrencyException) + { + throw new DomainException(ErrorCodes.ApprovalNotPending, $"Price adjustment {adjustment.DocNo} was just decided by someone else — reload it.", 409); + } + } + + private static int? PendingLevel(PriceAdjustment p) => p.Status switch + { + PriceAdjustmentStatus.PendingApproval => 1, + PriceAdjustmentStatus.PendingSecondApproval => 2, + _ => null + }; + + private static ApprovalTarget Target(PriceAdjustment p) => new( + ApprovalDocType, p.PriceAdjustmentId, p.DocNo, ApprovalNavCode, p.CreatedBy, p.RequiredApprovals, PendingLevel(p) ?? 1, p.ApprovedLevel1By); + + private static string Summary(PriceAdjustment p) + => $"Price adjustment {p.DocNo} ({p.Lines.Count} item{(p.Lines.Count == 1 ? "" : "s")}: {p.Reason})"; + + private async Task ToDtoAsync(PriceAdjustment p, CancellationToken ct) + { + var pending = PendingLevel(p); + var whyNot = pending is null ? "Not waiting for approval." : await _approvals.WhyCannotDecideAsync(Target(p), ct); + return new PriceAdjustmentDto( + p.PriceAdjustmentId, p.DocNo, p.Reason, p.Status, p.RequiredApprovals, pending, + p.CreatedBy, p.Creator?.DisplayName, p.CreatedAt, + p.ApprovedLevel1By, p.ApprovedLevel1At, p.ApprovedLevel2By, p.ApprovedLevel2At, + p.RejectedBy, p.RejectedAt, p.RejectReason, p.CancelledAt, p.AppliedAt, + p.Lines.OrderBy(l => l.PriceAdjustmentLineId).Select(l => new PriceAdjustmentLineDto( + l.PriceAdjustmentLineId, l.ItemId, l.Item?.Sku ?? $"#{l.ItemId}", l.Item?.Name ?? string.Empty, + l.OldMinPrice, l.OldMaxPrice, l.OldFlatPrice, l.NewMinPrice, l.NewMaxPrice, l.NewFlatPrice, + l.Item?.SellingMinPrice, l.Item?.SellingMaxPrice, l.Item?.SellingFlatPrice)).ToList(), + await _approvals.HistoryAsync(ApprovalDocType, p.PriceAdjustmentId, ct), + whyNot is null, whyNot, + pending is not null && p.CreatedBy == _currentUser.AuditUserId); + } +} diff --git a/Backend/ERPCore/Services/SalesInvoicePaymentService.cs b/Backend/ERPCore/Services/SalesInvoicePaymentService.cs index 5018125..9b74518 100644 --- a/Backend/ERPCore/Services/SalesInvoicePaymentService.cs +++ b/Backend/ERPCore/Services/SalesInvoicePaymentService.cs @@ -91,6 +91,7 @@ public sealed class SalesInvoicePaymentService : ISalesInvoicePaymentService PaymentDate = paymentDate, GlBankAccountId = account.AccountId, BankAccountName = account.AccountName, + PaymentMethod = ResolveMethod(request.PaymentMethod, account), Reference = request.Reference, GlJournalNo = posted.JournalNo, CreatedBy = actor, @@ -151,16 +152,17 @@ public sealed class SalesInvoicePaymentService : ISalesInvoicePaymentService // Oldest invoice first: walk the lines in order, filling each invoice's balance before the next. var ordered = invoices.OrderBy(i => i.InvoiceDate).ThenBy(i => i.SalesInvoiceId).ToList(); - var allocations = new List<(SalesInvoice Invoice, GlBankAccount Account, decimal Amount)>(); + var allocations = new List<(SalesInvoice Invoice, GlBankAccount Account, SalesPaymentMethod Method, decimal Amount)>(); var invIdx = 0; var invRemaining = ordered[0].BalanceAmount; foreach (var (line, account) in lines) { var lineRemaining = line.Amount; + var method = ResolveMethod(line.PaymentMethod, account); while (lineRemaining > 0) { var take = Math.Min(lineRemaining, invRemaining); - allocations.Add((ordered[invIdx], account, take)); + allocations.Add((ordered[invIdx], account, method, take)); lineRemaining -= take; invRemaining -= take; if (invRemaining == 0 && invIdx < ordered.Count - 1) @@ -196,7 +198,7 @@ public sealed class SalesInvoicePaymentService : ISalesInvoicePaymentService }, token); var entities = new List(); - foreach (var (invoice, account, amount) in allocations) + foreach (var (invoice, account, method, amount) in allocations) { var entity = new SalesInvoicePayment { @@ -205,6 +207,7 @@ public sealed class SalesInvoicePaymentService : ISalesInvoicePaymentService PaymentDate = paymentDate, GlBankAccountId = account.AccountId, BankAccountName = account.AccountName, + PaymentMethod = method, Reference = request.Reference, GlJournalNo = posted.JournalNo, CreatedBy = actor, @@ -238,6 +241,13 @@ public sealed class SalesInvoicePaymentService : ISalesInvoicePaymentService return (day, day >= today ? now : day.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc)); } + /// The caller's method, or — when omitted (older clients) — the account's kind: money into a + /// Cash account is cash, money into a Bank account is taken to be a card payment. + private static SalesPaymentMethod ResolveMethod(SalesPaymentMethod? requested, GlBankAccount account) + => requested ?? (string.Equals(account.AccountType, "Cash", StringComparison.OrdinalIgnoreCase) + ? SalesPaymentMethod.Cash + : SalesPaymentMethod.Card); + public async Task> ListAsync(int salesInvoiceId, CancellationToken ct = default) { if (!await _invoices.Query().AnyAsync(i => i.SalesInvoiceId == salesInvoiceId, ct)) @@ -248,11 +258,11 @@ public sealed class SalesInvoicePaymentService : ISalesInvoicePaymentService .OrderByDescending(p => p.SalesInvoicePaymentId) .Select(p => new SalesInvoicePaymentDto( p.SalesInvoicePaymentId, p.SalesInvoiceId, p.Amount, p.PaymentDate, - p.GlBankAccountId, p.BankAccountName, p.Reference, p.GlJournalNo, p.CreatedAt)) + p.GlBankAccountId, p.BankAccountName, p.Reference, p.GlJournalNo, p.CreatedAt, p.PaymentMethod)) .ToListAsync(ct); } private static SalesInvoicePaymentDto Map(SalesInvoicePayment p) => new( p.SalesInvoicePaymentId, p.SalesInvoiceId, p.Amount, p.PaymentDate, - p.GlBankAccountId, p.BankAccountName, p.Reference, p.GlJournalNo, p.CreatedAt); + p.GlBankAccountId, p.BankAccountName, p.Reference, p.GlJournalNo, p.CreatedAt, p.PaymentMethod); } diff --git a/Backend/ERPCore/Services/SalesMappingService.cs b/Backend/ERPCore/Services/SalesMappingService.cs index 1aafd88..50761de 100644 --- a/Backend/ERPCore/Services/SalesMappingService.cs +++ b/Backend/ERPCore/Services/SalesMappingService.cs @@ -26,7 +26,9 @@ public sealed class SalesMappingService : ISalesMappingService slip.TaxTotal, slip.GrandTotal, slip.PaidAmount, - slip.BalanceAmount); + slip.BalanceAmount, + slip.CashAmount, + slip.CardAmount); public SalesInvoiceDto MapInvoice(SalesInvoice invoice) => new( diff --git a/Backend/ERPCore/Services/SalesSlipService.cs b/Backend/ERPCore/Services/SalesSlipService.cs index fb8802e..9e713f9 100644 --- a/Backend/ERPCore/Services/SalesSlipService.cs +++ b/Backend/ERPCore/Services/SalesSlipService.cs @@ -142,7 +142,7 @@ public sealed class SalesSlipService : ISalesSlipService CreatedAt = DateTime.UtcNow, Lines = lines }; - Recalculate(entity, request.PaidAmount); + Recalculate(entity, request.PaidAmount, request.CardAmount); await _slips.AddAsync(entity, token); return entity; }, ct); @@ -163,7 +163,7 @@ public sealed class SalesSlipService : ISalesSlipService slip.CustomerSnapshotName = await _customers.Query().Where(x => x.CustomerId == request.CustomerId).Select(x => x.DisplayName ?? x.Name).FirstAsync(ct); slip.Lines.Clear(); foreach (var line in await BuildLinesAsync(request.WarehouseId, request.Lines, ct)) slip.Lines.Add(line); - Recalculate(slip, request.PaidAmount); + Recalculate(slip, request.PaidAmount, request.CardAmount); slip.UpdatedAt = DateTime.UtcNow; try { @@ -249,9 +249,9 @@ public sealed class SalesSlipService : ISalesSlipService /// GrandTotal, BalanceAmount == 0) — the behavior every slip had before partial payment /// existed, so omitting it is a no-op change. Must be clamped against the just-recomputed /// GrandTotal, not the request's own line totals, since GrandTotal is only known after - /// lines are summed here. + /// lines are summed here. null means all cash. /// - private static void Recalculate(SalesSlip slip, decimal? requestedPaidAmount) + private static void Recalculate(SalesSlip slip, decimal? requestedPaidAmount, decimal? requestedCardAmount) { slip.Subtotal = slip.Lines.Sum(x => x.Qty * x.UnitPrice); slip.DiscountTotal = slip.Lines.Sum(x => x.DiscountAmount); @@ -263,8 +263,15 @@ public sealed class SalesSlipService : ISalesSlipService throw new DomainException(ErrorCodes.SalesSlipPaidAmountInvalid, $"paidAmount must be between 0 and the slip's total {slip.GrandTotal}.", 400); + var card = requestedCardAmount ?? 0m; + if (card < 0 || card > paid) + throw new DomainException(ErrorCodes.SalesCardAmountInvalid, + $"cardAmount must be between 0 and the paid amount {paid}.", 400); + slip.PaidAmount = paid; slip.BalanceAmount = slip.GrandTotal - paid; + slip.CardAmount = card; + slip.CashAmount = paid - card; } private SalesSlipSummaryDto MapSummary(SalesSlip x) => new( diff --git a/Backend/ERPCore/Services/Stock/FifoCostingService.cs b/Backend/ERPCore/Services/Stock/FifoCostingService.cs index eeeb91e..18205e9 100644 --- a/Backend/ERPCore/Services/Stock/FifoCostingService.cs +++ b/Backend/ERPCore/Services/Stock/FifoCostingService.cs @@ -28,6 +28,7 @@ public sealed class FifoCostingService : IFifoCostingService private readonly IGeneralLedgerService _gl; private readonly IGlAccountConfigService _glAccounts; + /// Records each issue for the after-request low-stock check; optional so callers outside a request (tests) can omit it. public FifoCostingService( IRepository layers, IRepository ledger, IRepository journal, ErpDbContext db, @@ -312,6 +313,7 @@ public sealed class FifoCostingService : IFifoCostingService segments.Add(new ConsumedSegment(shortfall.LayerId, remaining, unitCost)); } + _lowStock?.Track(itemId, warehouseId, qtyBase); return segments; } diff --git a/Backend/ERPCore/Services/Stock/LowStockAlertService.cs b/Backend/ERPCore/Services/Stock/LowStockAlertService.cs new file mode 100644 index 0000000..37f9c63 --- /dev/null +++ b/Backend/ERPCore/Services/Stock/LowStockAlertService.cs @@ -0,0 +1,67 @@ +using ERPCore.Domain.Entities; +using ERPCore.Repositories.Interfaces; +using ERPCore.Services.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace ERPCore.Services.Stock; + +/// +/// Raises the stock.low notification when an issue takes an item's available stock at a +/// warehouse down to or below that warehouse's reorder point (). Only the +/// crossing raises it — stock that was already low doesn't alert again on every sale. Runs after +/// the request has committed (), so a rolled-back +/// document never alerts. +/// +public sealed class LowStockAlertService +{ + public const string ModuleKey = "stock.low"; + + private readonly LowStockTracker _tracker; + private readonly IRepository _reorders; + private readonly IStockService _stock; + private readonly INotificationService _notifications; + + public LowStockAlertService( + LowStockTracker tracker, IRepository reorders, IStockService stock, INotificationService notifications) + { + _tracker = tracker; + _reorders = reorders; + _stock = stock; + _notifications = notifications; + } + + public async Task CheckAsync(CancellationToken ct = default) + { + var issued = _tracker.Drain(); + if (issued.Count == 0) return; + + var itemIds = issued.Keys.Select(k => k.ItemId).Distinct().ToList(); + var rules = await _reorders.Query().AsNoTracking() + .Where(r => itemIds.Contains(r.ItemId)) + .Select(r => new + { + r.ItemId, r.WarehouseId, r.ReorderPoint, r.ReorderQty, + r.Item!.Sku, ItemName = r.Item.Name, Uom = r.Item.BaseUom!.Name, + WarehouseCode = r.Warehouse!.Code, WarehouseName = r.Warehouse.Name + }) + .ToListAsync(ct); + + foreach (var rule in rules) + { + if (!issued.TryGetValue((rule.ItemId, rule.WarehouseId), out var qty)) continue; + + var available = (await _stock.GetOnHandAsync(rule.ItemId, rule.WarehouseId, ct)).Available; + var before = available + qty; + if (available > rule.ReorderPoint || before <= rule.ReorderPoint) continue; + + await _notifications.RaiseAsync( + ModuleKey, + $"Low stock: {rule.ItemName}", + $"{rule.Sku} — {rule.ItemName} is down to {Qty(available)} {rule.Uom} at {rule.WarehouseCode} ({rule.WarehouseName}), " + + $"at or below its alert level of {Qty(rule.ReorderPoint)}. Suggested reorder: {Qty(rule.ReorderQty)} {rule.Uom}.", + nameof(Item), rule.ItemId, ct); + } + } + + private static string Qty(decimal value) => value.ToString("0.####"); +} diff --git a/Backend/ERPCore/Services/Stock/LowStockTracker.cs b/Backend/ERPCore/Services/Stock/LowStockTracker.cs new file mode 100644 index 0000000..cead6cd --- /dev/null +++ b/Backend/ERPCore/Services/Stock/LowStockTracker.cs @@ -0,0 +1,27 @@ +namespace ERPCore.Services.Stock; + +/// +/// Collects, for one request, every item/warehouse whose stock went down and by how much. +/// — the one path every stock issue takes (sales, +/// transfers, adjustments, returns, production) — records here; +/// checks the entries against their reorder rules once the request has committed. Has no +/// dependencies on purpose, so the FIFO service can take it without a DI cycle. +/// +public sealed class LowStockTracker +{ + private readonly Dictionary<(int ItemId, int WarehouseId), decimal> _issued = new(); + + public void Track(int itemId, int warehouseId, decimal qty) + { + if (qty <= 0) return; + _issued[(itemId, warehouseId)] = _issued.GetValueOrDefault((itemId, warehouseId)) + qty; + } + + /// Hands back what was recorded and starts empty again. + public IReadOnlyDictionary<(int ItemId, int WarehouseId), decimal> Drain() + { + var copy = new Dictionary<(int ItemId, int WarehouseId), decimal>(_issued); + _issued.Clear(); + return copy; + } +} diff --git a/Backend/ERPCore/System/Errors/ErrorCodes.cs b/Backend/ERPCore/System/Errors/ErrorCodes.cs index 10b002f..57297c0 100644 --- a/Backend/ERPCore/System/Errors/ErrorCodes.cs +++ b/Backend/ERPCore/System/Errors/ErrorCodes.cs @@ -93,6 +93,7 @@ public static class ErrorCodes // Sales slip partial payment (PaidAmount at ring-up time, not installments after the fact) public const string SalesSlipPaidAmountInvalid = "SALES_SLIP_PAID_AMOUNT_INVALID"; + public const string SalesCardAmountInvalid = "SALES_CARD_AMOUNT_INVALID"; // Purchase invoices (consolidate Confirmed GRNs; vendor payments now tracked here, not on the GRN) public const string GrnAlreadyInvoiced = "GRN_ALREADY_INVOICED"; @@ -104,4 +105,37 @@ public static class ErrorCodes public const string PurchaseInvoiceNotPayable = "PURCHASE_INVOICE_NOT_PAYABLE"; public const string PurchaseInvoicePaymentExceedsBalance = "PURCHASE_INVOICE_PAYMENT_EXCEEDS_BALANCE"; public const string PurchaseInvoiceBankAccountNotFound = "PURCHASE_INVOICE_BANK_ACCOUNT_NOT_FOUND"; + + // Expenses (docs/EXPENSE_MODULE_PLAN.md §6) + public const string ExpenseNotDraft = "EXPENSE_NOT_DRAFT"; + public const string ExpenseNotPayable = "EXPENSE_NOT_PAYABLE"; + public const string ExpensePaymentExceedsBalance = "EXPENSE_PAYMENT_EXCEEDS_BALANCE"; + public const string ExpenseHasPayments = "EXPENSE_HAS_PAYMENTS"; + public const string ExpenseTitleCategoryMismatch = "EXPENSE_TITLE_CATEGORY_MISMATCH"; + public const string ExpenseTitleInactive = "EXPENSE_TITLE_INACTIVE"; + public const string ExpenseSystemRowReadOnly = "EXPENSE_SYSTEM_ROW_READONLY"; + public const string ExpenseBankAccountNotFound = "EXPENSE_BANK_ACCOUNT_NOT_FOUND"; + public const string ExpenseChequeNoRequired = "EXPENSE_CHEQUE_NO_REQUIRED"; + public const string DuplicateExpenseCategory = "DUPLICATE_EXPENSE_CATEGORY"; + public const string DuplicateExpenseTitle = "DUPLICATE_EXPENSE_TITLE"; + + // Day-End Sales Report (docs/DAY_END_SALES_REPORT_PLAN.md) + public const string DayEndReportRangeInvalid = "DAY_END_REPORT_RANGE_INVALID"; + + // Bank deposits (docs/BANK_DEPOSIT_MODULE_PLAN.md §7.4) + public const string BankDepositCashAccountInvalid = "BANK_DEPOSIT_CASH_ACCOUNT_INVALID"; + public const string BankDepositBankAccountInvalid = "BANK_DEPOSIT_BANK_ACCOUNT_INVALID"; + public const string BankDepositExceedsCashInHand = "BANK_DEPOSIT_EXCEEDS_CASH_IN_HAND"; + public const string BankDepositDateInvalid = "BANK_DEPOSIT_DATE_INVALID"; + public const string BankDepositNotCancellable = "BANK_DEPOSIT_NOT_CANCELLABLE"; + + // Dual approval (docs/DUAL_APPROVAL_PLAN.md §4.2, §6.2) + public const string ApprovalForbidden = "APPROVAL_FORBIDDEN"; + public const string ApprovalSamePerson = "APPROVAL_SAME_PERSON"; + public const string ApprovalNotPending = "APPROVAL_NOT_PENDING"; + public const string ApprovalRoleRequired = "APPROVAL_ROLE_REQUIRED"; + public const string ApprovalReasonRequired = "APPROVAL_REASON_REQUIRED"; + public const string PriceAdjustmentItemPending = "PRICE_ADJUSTMENT_ITEM_PENDING"; + public const string PriceAdjustmentStale = "PRICE_ADJUSTMENT_STALE"; + public const string PriceChangeRequiresApproval = "PRICE_CHANGE_REQUIRES_APPROVAL"; } diff --git a/Backend/ERPCore/appsettings.Development.json b/Backend/ERPCore/appsettings.Development.json index 7ff3bb5..fb17e25 100644 --- a/Backend/ERPCore/appsettings.Development.json +++ b/Backend/ERPCore/appsettings.Development.json @@ -12,6 +12,6 @@ "BaseUrl": "http://localhost:5011" }, "GeneralLedgerService": { - "BaseUrl": "http://localhost:5200/api/v1/" + "BaseUrl": "https://localhost:7024/api/v1/" } } diff --git a/Backend/ERPCore/appsettings.json b/Backend/ERPCore/appsettings.json index b5c8f51..ec012c5 100644 --- a/Backend/ERPCore/appsettings.json +++ b/Backend/ERPCore/appsettings.json @@ -49,6 +49,16 @@ "SalesInvoice": { "GlAccountsReceivableCode": "1300" }, + "DayEndReport": { + "TimeZone": "Asia/Colombo" + }, + "Expense": { + "GlAccruedExpensesCode": "2300" + }, + "BankDeposit": { + "DefaultCashAccountCode": "1000-01", + "CashTrackingStartDate": "2026-10-01" + }, "PurchaseInvoice": { "GlClearingAccountCode": "2000", "GlAccountsPayableCode": "2200" diff --git a/Backend/PROGRESS.md b/Backend/PROGRESS.md index b80f704..7216d65 100644 --- a/Backend/PROGRESS.md +++ b/Backend/PROGRESS.md @@ -4,6 +4,114 @@ Legend: `[ ]` not started · `[~]` in progress · `[x]` done Spec: `docs/10-BACKEND-PHASE1.md` (model + rules) · `docs/11-BACKEND-PHASE1.md` (API) Convention: `docs/01-DOC-GUIDE.md §6`. Update this file in the **same commit** as the code. When ticking `[x]`, append a short note + any deviation. +## Dual approval — stock & price adjustments (2026-10-12) — `docs/DUAL_APPROVAL_PLAN.md` + +- [x] **Approval engine** — `IApprovalService`/`ApprovalService`: maker–checker rules in one place (requester can't + approve `APPROVAL_FORBIDDEN`; the two approvers must differ `APPROVAL_SAME_PERSON`; optional level-2 roles + `APPROVAL_ROLE_REQUIRED`; reject needs a reason `APPROVAL_REASON_REQUIRED`; no Admin bypass). One `DocumentApproval` + row per decision (`document_approvals`, polymorphic `DocType` ADJ/PADJ + `DocId`). `ApprovalConfig` singleton + (`approval_config`, seeded 2/2): levels 0/1/2 per document, a value (LKR) / percent threshold below which two levels + drop to one, `Level2RoleCodes`. Approvers are Admins plus roles with the Approve action on the screen's NAV code. + `INotificationService.NotifyUsersAsync` (targeted, same InApp gate) + modules `approval.pending/approved/rejected`. +- [x] **Stock adjustments** — `AdjustmentStatus` + `PendingSecondApproval`, `Rejected`, `Cancelled` (column widened to 30). + `CreateAsync` validates as before (reason, items, serials) then saves a **request**; stock, ledger, serial moves and the + GL journal happen in `PostInCurrentTransactionAsync` on the final approval, re-validated then (negative stock / moved + serials fail the approval, the request stays pending). Requested serials are kept as JSON on the line + (`RequestedSerialIds/Nos`) and new serials are only created at posting. Levels 0 = posts on create (old behaviour). + Value for the threshold = |qty| × average FIFO cost. Endpoints `POST /stock-adjustments/{id}/approve|reject|cancel` + (approve/reject need `stock.adjustments` Approve), `status` filter on the list. Wastage uses the same path. +- [x] **Stock counts** — `CountService.PostAsync` now submits its variances through `AdjustmentService.SubmitAsync` + (closing the count in the same transaction), so a count is not a way around approval. Its own copy of the posting/GL + code is gone. **Deviation:** serial moves for count variances are recorded against the adjustment, not the count. +- [x] **Price adjustments** — `PriceAdjustment` (`PADJ-…`), `PriceAdjustmentLine` (old snapshot + new min/max/flat), + `ItemPriceHistory`. Rules: one pending request per item (`PRICE_ADJUSTMENT_ITEM_PENDING`), min ≤ flat ≤ max, at least + one change; on final approval the item must still have the snapshot prices (`PRICE_ADJUSTMENT_STALE`), then prices are + written and a history row added. `PriceAdjustmentsController` (`/api/v1/price-adjustments` + `GET /items/{id}/price-history`). + `ItemService.UpdateAsync` refuses a price change while price approval is on (`PRICE_CHANGE_REQUIRES_APPROVAL`); prices + set on item **create** need no approval. +- [x] **Inbox & settings** — `ApprovalsController`: `GET /approvals/pending` (what you can approve + your own requests, + actionable count), `GET/PUT /approvals/config` (PUT needs `settings.approvals`). +- [x] **Migration `AddDualApproval`** — tables, columns, seeds (sub-nav 48 `products.price-adjustments`, 49 + `settings.approvals`; permissions 58/59), grants 58 to Admin + roles with Products (2/11), 59 to Admin, turns the three + approval notifications on. Existing adjustments keep `RequiredApprovals = 0` (posted without approval). **Not yet applied**. +- [x] **Tests** — `DualApprovalTests` (20 incl. theories): pending-until-approved, two different approvers, own-request and + same-person refusals, reject with reason, levels 0, value threshold, withdraw, level-2 roles, price apply + history, stale + price, one pending request per item. Suite 86/86. + +## Bank Deposit module (2026-10-08) — `docs/BANK_DEPOSIT_MODULE_PLAN.md` + +- [x] **Cash in hand** — `ICashInHandService`/`CashInHandService`: till cash per branch and business day. In = slip/bundle + `CashAmount` + cash invoice payments (any invoice); out = cash expense payments (expense not Cancelled); Posted deposits + reduce it by `BusinessDate`. Closing carries to the next day from `BankDeposit:CashTrackingStartDate` (appsettings, + `2026-10-01`); the opening before a window is a few DB `SUM`s. "Available to deposit" = lowest closing from that day + to today, so a back-dated deposit can't overdraw a later day. `BusinessDayClock` (singleton) now holds the + `DayEndReport:TimeZone` day ↔ UTC helpers for both this and the report. +- [x] **Entity + migration `AddBankDepositModule`** — `BankDeposit` (`bank_deposits`, branch query filter, `CHECK Amount > 0`, + index `(BranchId, BusinessDate, Status)`), enum `BankDepositStatus` (Posted/Cancelled, no draft), doc type `BD`. + Seeds sub-nav `accounts.bank-deposits` (SubNavItemId 47, SortOrder 6) + permission 57, granted (Actions 31) to Admin and + every role holding an Accounts permission (26/32/33/34/54). **Not yet applied** — applies on next startup (`MigrateAsync`). +- [x] **API** — `BankDepositsController` (`/api/v1/bank-deposits`): list (business-date range, branch, bank account, status; + returns `totalAmount` of all matching rows), get, `cash-in-hand?date&branchId`, `cash-in-hand/daily?from&to` (≤ 92 days), + create, `{id}/cancel`. **Deviation from plan §6:** one `NAV:accounts.bank-deposits` permission with action suffixes + (create = `Create`, cancel = `Delete`) instead of three permission codes — the project's RBAC is one NAV code per screen. +- [x] **GL** — create: Dr bank account / Cr cash account (`BANK_DEPOSIT`, entry date = deposit date), posted before commit. + Cancel: reversing entry dated today. From must be a GL `Cash` account, to a `Bank` account. The balance check runs + inside the transaction after `SELECT … FOR UPDATE` on the branch row, so concurrent deposits for one branch serialize. + With branching off, cash is measured across all branches (everything sits on the default branch). +- [x] **Day-End Report** — cash figures now come from `ICashInHandService`; day rows add `openingCashInHand`, + `cashDeposited`, `closingCashInHand` (range totals: first opening, summed deposits, last closing); the day adds + `deposits[]`. `NetCash` unchanged. +- [x] **Tests** — `CashInHandServiceTests` (7), `BankDepositServiceTests` (14 incl. theories), plus a deposit case in + `DayEndReportServiceTests`. Suite: 59/59 passing. + +## Day-End Sales Report (2026-10-08) — `docs/DAY_END_SALES_REPORT_PLAN.md` + +- [x] **Payment method on sales** — enum `SalesPaymentMethod` (Cash/Card/BankTransfer/Cheque). `SalesInvoicePayment.PaymentMethod` + (request field optional on single and bulk payments; omitted ⇒ derived from the account: Cash account ⇒ Cash, Bank ⇒ Card). + `SalesSlip` and `BundleSale` gain `CashAmount`/`CardAmount` (request `cardAmount`, optional, rest is cash; error + `SALES_CARD_AMOUNT_INVALID`). **Deviation:** the UI only creates invoices, so the cash/card split that matters is the + per-payment method; the slip/bundle fields exist for API callers. +- [x] **Migration `AddSalesPaymentMethodsAndDayEndReport`** — columns above; backfills slips/bundles as all cash and + invoice payments as Cash (column default); seeds sub-nav `sales.day-end-report` (SubNavItemId 46) + permission 56 and + grants it to Admin and every role holding `NAV:sales.day-end` (52). **Not yet applied** — applies on next startup + (`MigrateAsync`). +- [x] **API** — `DayEndReportController` (`GET /api/v1/day-end-report?date&branchId`, `GET /api/v1/day-end-report/daily?from&to&branchId`, + ≤ 92 days, `DAY_END_REPORT_RANGE_INVALID`). `DayEndReportService`: Posted slips + bundles + invoices; Paid split by + method; counts cash/card/mixed/other/unpaid; Posted expenses by `ExpenseDate`; Income = sales − expenses; plus + collections on earlier invoices and net cash (till check). Days are `DayEndReport:TimeZone` (appsettings, + `Asia/Colombo`) calendar days. **Deviations:** sales returns are not netted off income — `SalesReturnLine` carries + no price; rows are bucketed by local day in memory (slim DB projections) since MariaDB can't group by a zone-shifted + date portably. Non-admins are forced to their own branch. +- [x] **Sales list + profit/loss** (2026-10-08) — the day response adds `sales[]` (doc type/no., time, customer, total, + paid, due, paid-by, cost, gross profit) and every day row adds `costOfGoodsSold` (stock ledger Out value per sale + document — same source as `SalesDayEndService`'s COGS), `grossProfit` (sales − cost) and `netProfit` (gross − expenses; + negative = loss). +- [x] **Tests** — `ERPCore.Tests/DayEndReportServiceTests.cs` (mixed documents and methods, cancelled expense excluded, + time-zone boundary, earlier-invoice collection + net cash, unpaid invoice, zero-filled days, range validation). + +- [x] **Entities + migration `AddExpenseModule`** — `ExpenseCategory`, `ExpenseTitle`, `Expense`, `ExpensePayment` + (`Domain/Entities/ExpenseCategory.cs`, `Expense.cs`), enums in `Domain/Enums/ExpenseStatus.cs`. Seeds 4 system + categories (Utility/Rental/Salaries/Other) + 18 system titles with **placeholder** GL codes 6100/6200/6300/6900 + (`ExpenseConfiguration.cs`). `Expense` is branch-scoped (global query filter) and numbered `EXP-…` per branch. +- [x] **Nav/permissions** — sub-items `accounts.expenses` (SubNavItemId 44) and `accounts.expense-setup` (45), + permissions 54/55. **Deviation from the plan:** ids 23/24 were already taken. +- [x] **API** — `ExpensesController` (`/api/v1/expenses`, list/summary/get/create/update/delete/post/post-and-pay/cancel/payments) + and `ExpenseSetupController` (`/api/v1/expense-categories`, `/api/v1/expense-titles`). Reading the setup lists needs + only `accounts`; changing them needs `accounts.expense-setup`. +- [x] **GL** — Post: Dr title's account / Cr `Expense:GlAccruedExpensesCode` (appsettings, placeholder `2300`). + Payment: Dr Accrued / Cr each cash-bank account. Cancel of a Posted expense with no payments posts the reversal. + All GL calls happen before commit (same pattern as `PurchaseInvoicePaymentService`). **Deviations:** a payment + submission may carry several lines (split cash/bank) and posts **one** journal shared by its payment rows; the + per-category default GL codes live in the seed, not in appsettings (config can't feed `HasData`); a Draft can also + be cancelled (no GL entry). Post & Pay accepts a full or partial payment (changed 2026-10-08; it first + required the full amount). The remainder stays as the balance. +- [x] **Migrations applied to dev DB** (2026-10-08): `AddExpenseModule`, `GrantExpensePermissions` (grants 54/55 to + Admin and to every role that already reaches Accounts; without it no role had the codes in `/auth/me`, so the + sidebar hid the screens), `UpdateExpenseGlCodes`. +- [x] **GL accounts created in the dev GL** (2026-10-08): the placeholder 6100 failed with "Account 6100 not found", + and GL generates codes itself. Created `2300` Accrued Expenses and `5200` Operating Expenses (control) with + `5200-01..04` (Utilities, Rent, Salaries & Wages, Other Expenses). The seeded titles now use those codes. + ## Per-variant content size (2026-08-11) — follow-up to the UOM re-model below The UOM re-model put `ContentQty`/`ContentUnit` on `Item` correctly, but the item **create page diff --git a/Frontend/PROGRESS.md b/Frontend/PROGRESS.md index 627db79..0ac6d5a 100644 --- a/Frontend/PROGRESS.md +++ b/Frontend/PROGRESS.md @@ -4,6 +4,68 @@ Legend: `[ ]` not started · `[~]` in progress · `[x]` done Spec: `docs/20-FRONTEND.md` (flows + rules) · `docs/11-BACKEND-PHASE1.md` (API contract) Convention: `docs/01-DOC-GUIDE.md §6`. Update this file in the **same commit** as the code. When ticking `[x]`, append a short note + any deviation. +## Dual approval — stock & price adjustments (2026-10-12) — `docs/DUAL_APPROVAL_PLAN.md` + +- [x] **Approvals inbox** (`app/dashboard/approvals`, sidebar entry for everyone with a badge of actionable requests): + "Waiting for you" (Approve / Approve & apply / Reject with reason) and "Waiting for others" (your own requests, with why). +- [x] **Stock → Adjustments**: status filter, rows open the new detail page `adjustments/[id]` (lines, requested serials, + approval timeline, Approve / Reject / Withdraw via `components/approvals/ApprovalPanel.tsx`). New adjustment, Wastage and + Count post now say "submitted for approval" with a link when the result is pending. Wastage list hides rejected/withdrawn + requests and tags pending ones. `AdjustmentStatusBadge` covers the new statuses. +- [x] **Products → Price Adjustments** (list, `new` — add items, edit new min/max/flat from the current prices, reason — + and `[id]` with old → new, a stale-request warning and the approval panel). Item page: selling prices read-only in Edit + while price approval is on, "Request price change" links, and a Price history panel. +- [x] **Settings → Approvals** (`app/dashboard/settings/approvals`): levels per document, thresholds, level-2 roles. +- [x] Types `types/approvals.ts`, API `lib/api/approvals.ts`; `lib/api/stock-adjustments.ts` gains approve/reject/cancel. + `tsc` clean; no new lint errors (the setState-in-effect errors in touched pages were already there). + +## Bank Deposit module (2026-10-08) — `docs/BANK_DEPOSIT_MODULE_PLAN.md` + +- [x] **Accounts → Bank Deposits** (`app/dashboard/accounts/bank-deposits`): list (search, status, business-date range, + bank-account filter, server total in the footer, CSV export) with a "Cash in hand by day" view (opening, cash in, + cash out, deposited, closing; the filter's range capped at 92 days, default last 30). Cards: deposited (filtered), + cash in hand now, net cash today. Admins' header branch switcher is sent as `branchId` (totals are server-side). +- [x] `new/`: branch (admins), business date, deposit date, from cash account (preselects `defaultCashAccountCode`), + to bank account, amount (defaults to "available to deposit", blocked above it), slip no., depositor, remarks; a cash + position panel beside it. `[id]/`: details + cancel with reason (reverses GL). `lib/api/bank-deposits.ts`, + `types/bank-deposits.ts`, `components/accounts/bank-deposit-badges.tsx`. Sidebar + Accounts hub card added. +- [x] **Day-End Report**: the one-line "Net cash in hand" became a Cash in hand group (opening, net cash today, + deposited to bank, remaining — amber while > 0) with the day's deposits table; the daily table adds Deposited and + Cash in hand columns. PDFs: single day adds the cash block + deposits; day-wise adds a separate "Cash in hand and bank + deposits" table. **Deviation:** not two more columns on the day-wise PDF table — A4 portrait is already full at 9 columns. + +## Day-End Sales Report (2026-10-08) — `docs/DAY_END_SALES_REPORT_PLAN.md` + +- [x] Sales payment method: `PaymentLinesEditor` takes optional `methodOptions`/`methodForAccount` and shows a Method column + (picking a Cash account sets Cash, a Bank account sets Card; changeable). Used by `SalesInvoicePaymentDialog` and + `SalesInvoiceBulkPaymentDialog` only — the expense/purchase dialogs are unchanged. Types in `types/sales.ts`, `types/bundles.ts`. +- [x] **Sales → Day-End Report** (`app/dashboard/sales/day-end-report`): business-date picker, sales/payment/count cards, + Income card (sales − expenses), the day's expenses, and a day-wise section (from/to, default last 30 days) with an + income/sales/expenses line chart (`components/sales/DayEndIncomeChart.tsx`) and a daily table with totals (click a + row to open that day). Admins follow the header branch switcher. `lib/api/day-end-report.ts`. +- [x] PDFs (`lib/pdf/day-end-report-pdf.ts`): day-wise income (totals, chart, paginated daily table + totals row) and + single day (figures, counts, expense list). **Deviation:** tables drawn with plain `jspdf` instead of adding + `jspdf-autotable`; the chart is rendered offscreen in light colours for print. +- [x] Sidebar entry (`sales.day-end-report`) and header title. +- [x] **Sales table + profit/loss** (2026-10-08): the day view lists every sale (time, no., type, customer, paid by, total, + paid, due, cost, profit; invoices link to their page) and ends with a statement — sales − cost of goods sold = gross + profit − expenses = **Net profit** (green) or **Net loss** (red, shown as a positive amount labelled Loss; + `profitOrLoss()` in `lib/format.ts`). Day-wise table, chart and both PDFs gain a Profit / loss column/line. + +- [x] `types/expenses.ts`, `lib/api/expenses.ts` (`expensesApi`, `expenseSetupApi`). +- [x] **Accounts → Expenses** (`app/dashboard/accounts/expenses`): list with period summary cards (posted, unpaid, + outstanding, overdue), by-category breakdown, filters (status tabs, payment status incl. Overdue, category → title, + date range, search), CSV export of all pages, row Post / Pay / Post & Pay. `new`, `[id]` (detail, payments, cancel + with reason, delete draft) and `[id]/edit` (Draft only), sharing `components/accounts/ExpenseForm.tsx`. + "Save & pay" lands on the detail page with `?pay=1`, which opens the payment dialog once. +- [x] `components/accounts/ExpensePaymentDialog.tsx` reuses `PaymentLinesEditor` (split across cash/bank accounts) plus + payment method and cheque no. A Draft goes through the single-transaction `post-and-pay` endpoint, unlike + `PurchaseInvoicePaymentDialog`'s post-then-pay-per-line loop. +- [x] **Accounts → Expense Setup** (`app/dashboard/accounts/expense-setup`): categories → titles, add category/title, + edit (built-in names locked), GL code per title, activate/deactivate. Shows the Salaries/payroll double-count warning + (plan §10 Q4). The expense form has an inline "Add title" for users with `accounts.expense-setup`. +- [x] Sidebar (`AppSidebar.tsx`), header titles (`Header.tsx`) and the Accounts hub page list the two new screens. + ## 0. Foundation - [x] **Transport: same-origin Next `rewrites()` proxy** (`next.config.ts`, `/api/*` → `BACKEND_ORIGIN`, default `http://localhost:5224`). `BACKEND_ORIGIN` in `.env.local` / `.env.local.example` — **not** `NEXT_PUBLIC_*`; the browser never sees the backend URL. Chosen over backend CORS because it makes the API same-origin, so CORS and the Secure/SameSite=Strict session cookies stop being problems at all. (`.gitignore`'s `.env*` was silently swallowing the example file — added a `!.env.local.example` negation.) - [x] **Typed API client rebuilt** (`lib/api-client.ts`, 2026-07-17) — recovered the pre-deletion version from git (`0e4bcf1^`) and adapted: relative `/api/v1` base, **`credentials: "include"`** (never present before), `ApiResult`/`ProblemDetails` imported from `@/types/common` rather than redeclared, `readCsrfToken()` for the eight `[ValidateCsrf]` auth actions. `ApiError`, `apiRequest`, `apiRequestWithETag`, `buildQuery`, `ifMatch`/`idempotencyKey` all carried over. diff --git a/Frontend/erp-system/app/dashboard/accounts/bank-deposits/[id]/page.tsx b/Frontend/erp-system/app/dashboard/accounts/bank-deposits/[id]/page.tsx new file mode 100644 index 0000000..9a48ff1 --- /dev/null +++ b/Frontend/erp-system/app/dashboard/accounts/bank-deposits/[id]/page.tsx @@ -0,0 +1,190 @@ +"use client" + +import { use, useEffect, useState } from "react" +import Link from "next/link" +import { ArrowLeft, Ban } from "lucide-react" + +import { bankDepositsApi } from "@/lib/api/bank-deposits" +import { errorMessage } from "@/lib/error-map" +import { formatAmount, formatReportDate } from "@/lib/format" +import { cn } from "@/lib/utils" +import { BankDeposit } from "@/types/bank-deposits" + +import { BankDepositStatusBadge } from "@/components/accounts/bank-deposit-badges" +import { Button } from "@/components/ui/button" +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog" +import { Field, FieldError, FieldLabel } from "@/components/ui/field" +import { Skeleton } from "@/components/ui/skeleton" +import { Textarea } from "@/components/ui/textarea" +import { toast } from "@/components/ui/toast" + +export default function BankDepositDetailPage({ params }: { params: Promise<{ id: string }> }) { + const { id } = use(params) + const bankDepositId = Number(id) + + const [deposit, setDeposit] = useState(null) + const [loadError, setLoadError] = useState(null) + const error = Number.isFinite(bankDepositId) ? loadError : `Invalid deposit id '${id}'.` + const [cancelOpen, setCancelOpen] = useState(false) + const [cancelReason, setCancelReason] = useState("") + const [cancelError, setCancelError] = useState(null) + const [cancelling, setCancelling] = useState(false) + + useEffect(() => { + if (!Number.isFinite(bankDepositId)) return + bankDepositsApi + .get(bankDepositId) + .then(setDeposit) + .catch((err) => setLoadError(errorMessage(err))) + }, [bankDepositId]) + + async function handleCancel() { + if (!deposit) return + if (!cancelReason.trim()) { + setCancelError("Enter a reason") + return + } + setCancelling(true) + try { + const cancelled = await bankDepositsApi.cancel(deposit.bankDepositId, cancelReason.trim()) + setDeposit(cancelled) + setCancelOpen(false) + toast.success("Deposit cancelled", `Reversing journal ${cancelled.glReversalJournalNo ?? "—"} — the cash is back in hand.`) + } catch (err) { + setCancelError(errorMessage(err)) + } finally { + setCancelling(false) + } + } + + if (error) { + return ( +
+ +
{error}
+
+ ) + } + if (!deposit) return + + return ( +
+ + +
+
+
+

{deposit.docNo}

+ +
+

+ Cash of {formatReportDate(deposit.businessDate)} deposited to {deposit.toBankAccountName} +

+
+ + {deposit.status === "Posted" && ( + + )} +
+ +
+
+

Amount

+

+ {formatAmount(deposit.amount)} +

+
+
+

From

+

{deposit.fromCashAccountName}

+

GL {deposit.fromCashAccountCode}

+
+
+

To

+

{deposit.toBankAccountName}

+

+ {[deposit.toBankName, deposit.toAccountNumber, `GL ${deposit.toBankAccountCode}`].filter(Boolean).join(" · ")} +

+
+
+ +
+
+ + + + + + + + {deposit.status === "Cancelled" && ( + <> + + + + + )} + {deposit.remarks && ( +
+
Remarks
+
{deposit.remarks}
+
+ )} +
+
+ + + + + Cancel deposit {deposit.docNo} + +

+ A reversing journal entry will be posted (cash up, bank down) and {formatAmount(deposit.amount)} goes back into the cash in hand + for {formatReportDate(deposit.businessDate)}. This cannot be undone — record a new deposit if it was only entered wrongly. +

+ + Reason +