@tailor-platform/erp-kit 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +10 -10
- package/dist/cli.mjs +407 -69
- package/package.json +1 -1
- package/skills/erp-kit-app-1-requirements/SKILL.md +33 -17
- package/skills/erp-kit-app-2-requirements-review/SKILL.md +12 -0
- package/skills/erp-kit-app-3-plan/SKILL.md +18 -4
- package/skills/erp-kit-app-3-plan/references/resolver-extraction.md +1 -1
- package/skills/erp-kit-app-3-plan/references/screen-extraction.md +1 -1
- package/skills/erp-kit-app-4-plan-review/SKILL.md +12 -0
- package/skills/erp-kit-app-5-impl-backend/SKILL.md +12 -0
- package/skills/erp-kit-app-6-impl-frontend/SKILL.md +12 -0
- package/skills/erp-kit-app-7-impl-review/SKILL.md +13 -1
- package/skills/erp-kit-app-shared/references/progress-protocol.md +77 -0
- package/skills/erp-kit-mock-scenario/SKILL.md +1 -1
- package/skills/erp-kit-module-1-requirements/SKILL.md +1 -1
- package/skills/erp-kit-module-3-plan/SKILL.md +3 -3
- package/skills/erp-kit-module-3-update-plan/SKILL.md +3 -3
- package/skills/erp-kit-module-5-impl/SKILL.md +1 -1
- package/src/commands/app/index.ts +2 -0
- package/src/commands/app/progress/git-context.ts +16 -0
- package/src/commands/app/progress/index.ts +45 -0
- package/src/commands/app/progress/log.ts +49 -0
- package/src/commands/app/progress/progress.test.ts +128 -0
- package/src/commands/app/progress/schema-cmd.ts +10 -0
- package/src/commands/check.test.ts +4 -4
- package/src/commands/lib/discovery.test.ts +5 -7
- package/src/commands/lib/discovery.ts +8 -8
- package/src/commands/lib/sync-check-source.test.ts +1 -1
- package/src/commands/lib/sync-check-source.ts +6 -1
- package/src/commands/lib/sync-check-tests.test.ts +43 -0
- package/src/commands/lib/sync-check-tests.ts +20 -2
- package/src/commands/sync-check.ts +3 -0
- package/src/generator/generate-app-code.test.ts +0 -6
- package/src/generator/generate-app-code.ts +3 -13
- package/src/generator/generate-code.test.ts +10 -40
- package/src/generator/generate-code.ts +6 -12
- package/src/generator/stub-templates.test.ts +0 -7
- package/src/generator/stub-templates.ts +0 -14
- package/src/modules/finance-ledger/README.md +50 -0
- package/src/modules/finance-ledger/command/.gitkeep +0 -0
- package/src/modules/finance-ledger/command/addJournalLine.generated.ts +6 -0
- package/src/modules/finance-ledger/command/addJournalLine.test.ts +438 -0
- package/src/modules/finance-ledger/command/addJournalLine.ts +122 -0
- package/src/modules/finance-ledger/command/approveAndLockPeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/approveAndLockPeriod.test.ts +107 -0
- package/src/modules/finance-ledger/command/approveAndLockPeriod.ts +72 -0
- package/src/modules/finance-ledger/command/beginClose.generated.ts +6 -0
- package/src/modules/finance-ledger/command/beginClose.test.ts +106 -0
- package/src/modules/finance-ledger/command/beginClose.ts +58 -0
- package/src/modules/finance-ledger/command/closePeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/closePeriod.test.ts +87 -0
- package/src/modules/finance-ledger/command/closePeriod.ts +44 -0
- package/src/modules/finance-ledger/command/createAccountingPeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/createAccountingPeriod.test.ts +425 -0
- package/src/modules/finance-ledger/command/createAccountingPeriod.ts +133 -0
- package/src/modules/finance-ledger/command/createFiscalYear.generated.ts +6 -0
- package/src/modules/finance-ledger/command/createFiscalYear.test.ts +197 -0
- package/src/modules/finance-ledger/command/createFiscalYear.ts +70 -0
- package/src/modules/finance-ledger/command/createJournalEntry.generated.ts +6 -0
- package/src/modules/finance-ledger/command/createJournalEntry.test.ts +261 -0
- package/src/modules/finance-ledger/command/createJournalEntry.ts +121 -0
- package/src/modules/finance-ledger/command/deleteAccountingPeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/deleteAccountingPeriod.test.ts +71 -0
- package/src/modules/finance-ledger/command/deleteAccountingPeriod.ts +55 -0
- package/src/modules/finance-ledger/command/deleteFiscalYear.generated.ts +6 -0
- package/src/modules/finance-ledger/command/deleteFiscalYear.test.ts +38 -0
- package/src/modules/finance-ledger/command/deleteFiscalYear.ts +34 -0
- package/src/modules/finance-ledger/command/deleteJournalEntry.generated.ts +6 -0
- package/src/modules/finance-ledger/command/deleteJournalEntry.test.ts +58 -0
- package/src/modules/finance-ledger/command/deleteJournalEntry.ts +43 -0
- package/src/modules/finance-ledger/command/executeYearEndClose.generated.ts +6 -0
- package/src/modules/finance-ledger/command/executeYearEndClose.test.ts +239 -0
- package/src/modules/finance-ledger/command/executeYearEndClose.ts +415 -0
- package/src/modules/finance-ledger/command/finalCloseAndLockPeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/finalCloseAndLockPeriod.test.ts +102 -0
- package/src/modules/finance-ledger/command/finalCloseAndLockPeriod.ts +76 -0
- package/src/modules/finance-ledger/command/finalizeFinancialStatement.generated.ts +6 -0
- package/src/modules/finance-ledger/command/finalizeFinancialStatement.test.ts +73 -0
- package/src/modules/finance-ledger/command/finalizeFinancialStatement.ts +73 -0
- package/src/modules/finance-ledger/command/generateFinancialStatement.generated.ts +6 -0
- package/src/modules/finance-ledger/command/generateFinancialStatement.test.ts +311 -0
- package/src/modules/finance-ledger/command/generateFinancialStatement.ts +275 -0
- package/src/modules/finance-ledger/command/generatePreliminaryStatements.generated.ts +6 -0
- package/src/modules/finance-ledger/command/generatePreliminaryStatements.test.ts +152 -0
- package/src/modules/finance-ledger/command/generatePreliminaryStatements.ts +140 -0
- package/src/modules/finance-ledger/command/generateTrialBalance.generated.ts +6 -0
- package/src/modules/finance-ledger/command/generateTrialBalance.test.ts +439 -0
- package/src/modules/finance-ledger/command/generateTrialBalance.ts +268 -0
- package/src/modules/finance-ledger/command/initiatePeriodClose.generated.ts +6 -0
- package/src/modules/finance-ledger/command/initiatePeriodClose.test.ts +153 -0
- package/src/modules/finance-ledger/command/initiatePeriodClose.ts +84 -0
- package/src/modules/finance-ledger/command/openForAdvanceEntry.generated.ts +6 -0
- package/src/modules/finance-ledger/command/openForAdvanceEntry.test.ts +87 -0
- package/src/modules/finance-ledger/command/openForAdvanceEntry.ts +44 -0
- package/src/modules/finance-ledger/command/openPeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/openPeriod.test.ts +90 -0
- package/src/modules/finance-ledger/command/openPeriod.ts +44 -0
- package/src/modules/finance-ledger/command/permanentlyClosePeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/permanentlyClosePeriod.test.ts +87 -0
- package/src/modules/finance-ledger/command/permanentlyClosePeriod.ts +48 -0
- package/src/modules/finance-ledger/command/postAdjustingEntries.generated.ts +6 -0
- package/src/modules/finance-ledger/command/postAdjustingEntries.test.ts +392 -0
- package/src/modules/finance-ledger/command/postAdjustingEntries.ts +156 -0
- package/src/modules/finance-ledger/command/postJournalEntry.generated.ts +6 -0
- package/src/modules/finance-ledger/command/postJournalEntry.test.ts +346 -0
- package/src/modules/finance-ledger/command/postJournalEntry.ts +160 -0
- package/src/modules/finance-ledger/command/processInventoryHandoff.generated.ts +6 -0
- package/src/modules/finance-ledger/command/processInventoryHandoff.test.ts +211 -0
- package/src/modules/finance-ledger/command/processInventoryHandoff.ts +133 -0
- package/src/modules/finance-ledger/command/processManufacturingHandoff.generated.ts +6 -0
- package/src/modules/finance-ledger/command/processManufacturingHandoff.test.ts +221 -0
- package/src/modules/finance-ledger/command/processManufacturingHandoff.ts +133 -0
- package/src/modules/finance-ledger/command/processPurchaseHandoff.generated.ts +6 -0
- package/src/modules/finance-ledger/command/processPurchaseHandoff.test.ts +222 -0
- package/src/modules/finance-ledger/command/processPurchaseHandoff.ts +133 -0
- package/src/modules/finance-ledger/command/processSalesHandoff.generated.ts +6 -0
- package/src/modules/finance-ledger/command/processSalesHandoff.test.ts +257 -0
- package/src/modules/finance-ledger/command/processSalesHandoff.ts +135 -0
- package/src/modules/finance-ledger/command/regenerateFinancialStatement.generated.ts +6 -0
- package/src/modules/finance-ledger/command/regenerateFinancialStatement.test.ts +129 -0
- package/src/modules/finance-ledger/command/regenerateFinancialStatement.ts +186 -0
- package/src/modules/finance-ledger/command/removeJournalLine.generated.ts +6 -0
- package/src/modules/finance-ledger/command/removeJournalLine.test.ts +65 -0
- package/src/modules/finance-ledger/command/removeJournalLine.ts +39 -0
- package/src/modules/finance-ledger/command/reopenPeriod.generated.ts +6 -0
- package/src/modules/finance-ledger/command/reopenPeriod.test.ts +87 -0
- package/src/modules/finance-ledger/command/reopenPeriod.ts +44 -0
- package/src/modules/finance-ledger/command/reverseJournalEntry.generated.ts +6 -0
- package/src/modules/finance-ledger/command/reverseJournalEntry.test.ts +337 -0
- package/src/modules/finance-ledger/command/reverseJournalEntry.ts +140 -0
- package/src/modules/finance-ledger/command/revertSoftLock.generated.ts +6 -0
- package/src/modules/finance-ledger/command/revertSoftLock.test.ts +96 -0
- package/src/modules/finance-ledger/command/revertSoftLock.ts +67 -0
- package/src/modules/finance-ledger/command/updateFiscalYear.generated.ts +6 -0
- package/src/modules/finance-ledger/command/updateFiscalYear.test.ts +138 -0
- package/src/modules/finance-ledger/command/updateFiscalYear.ts +85 -0
- package/src/modules/finance-ledger/command/updateJournalEntry.generated.ts +6 -0
- package/src/modules/finance-ledger/command/updateJournalEntry.test.ts +195 -0
- package/src/modules/finance-ledger/command/updateJournalEntry.ts +86 -0
- package/src/modules/finance-ledger/command/updateJournalLine.generated.ts +6 -0
- package/src/modules/finance-ledger/command/updateJournalLine.test.ts +385 -0
- package/src/modules/finance-ledger/command/updateJournalLine.ts +155 -0
- package/src/modules/finance-ledger/command/verifySubledgerTransfers.generated.ts +6 -0
- package/src/modules/finance-ledger/command/verifySubledgerTransfers.test.ts +201 -0
- package/src/modules/finance-ledger/command/verifySubledgerTransfers.ts +113 -0
- package/src/modules/finance-ledger/command/verifyTrialBalance.generated.ts +6 -0
- package/src/modules/finance-ledger/command/verifyTrialBalance.test.ts +136 -0
- package/src/modules/finance-ledger/command/verifyTrialBalance.ts +97 -0
- package/src/modules/finance-ledger/db/.gitkeep +0 -0
- package/src/modules/finance-ledger/db/accountingPeriod.ts +58 -0
- package/src/modules/finance-ledger/db/financialStatement.ts +92 -0
- package/src/modules/finance-ledger/db/financialStatementLineItem.ts +76 -0
- package/src/modules/finance-ledger/db/fiscalYear.ts +41 -0
- package/src/modules/finance-ledger/db/journalEntry.ts +101 -0
- package/src/modules/finance-ledger/db/journalLine.ts +64 -0
- package/src/modules/finance-ledger/db/periodClose.ts +97 -0
- package/src/modules/finance-ledger/db/trialBalance.ts +63 -0
- package/src/modules/finance-ledger/db/trialBalanceLine.ts +63 -0
- package/src/modules/finance-ledger/docs/commands/AddJournalLine.md +74 -0
- package/src/modules/finance-ledger/docs/commands/ApproveAndLockPeriod.md +53 -0
- package/src/modules/finance-ledger/docs/commands/BeginClose.md +47 -0
- package/src/modules/finance-ledger/docs/commands/ClosePeriod.md +45 -0
- package/src/modules/finance-ledger/docs/commands/CreateAccountingPeriod.md +69 -0
- package/src/modules/finance-ledger/docs/commands/CreateFiscalYear.md +56 -0
- package/src/modules/finance-ledger/docs/commands/CreateJournalEntry.md +63 -0
- package/src/modules/finance-ledger/docs/commands/DeleteAccountingPeriod.md +46 -0
- package/src/modules/finance-ledger/docs/commands/DeleteFiscalYear.md +40 -0
- package/src/modules/finance-ledger/docs/commands/DeleteJournalEntry.md +44 -0
- package/src/modules/finance-ledger/docs/commands/ExecuteYearEndClose.md +81 -0
- package/src/modules/finance-ledger/docs/commands/FinalCloseAndLockPeriod.md +49 -0
- package/src/modules/finance-ledger/docs/commands/FinalizeFinancialStatement.md +43 -0
- package/src/modules/finance-ledger/docs/commands/GenerateFinancialStatement.md +86 -0
- package/src/modules/finance-ledger/docs/commands/GeneratePreliminaryStatements.md +53 -0
- package/src/modules/finance-ledger/docs/commands/GenerateTrialBalance.md +75 -0
- package/src/modules/finance-ledger/docs/commands/InitiatePeriodClose.md +58 -0
- package/src/modules/finance-ledger/docs/commands/OpenForAdvanceEntry.md +44 -0
- package/src/modules/finance-ledger/docs/commands/OpenPeriod.md +45 -0
- package/src/modules/finance-ledger/docs/commands/PermanentlyClosePeriod.md +45 -0
- package/src/modules/finance-ledger/docs/commands/PostAdjustingEntries.md +61 -0
- package/src/modules/finance-ledger/docs/commands/PostJournalEntry.md +81 -0
- package/src/modules/finance-ledger/docs/commands/ProcessInventoryHandoff.md +72 -0
- package/src/modules/finance-ledger/docs/commands/ProcessManufacturingHandoff.md +68 -0
- package/src/modules/finance-ledger/docs/commands/ProcessPurchaseHandoff.md +68 -0
- package/src/modules/finance-ledger/docs/commands/ProcessSalesHandoff.md +71 -0
- package/src/modules/finance-ledger/docs/commands/RegenerateFinancialStatement.md +60 -0
- package/src/modules/finance-ledger/docs/commands/RemoveJournalLine.md +42 -0
- package/src/modules/finance-ledger/docs/commands/ReopenPeriod.md +45 -0
- package/src/modules/finance-ledger/docs/commands/ReverseJournalEntry.md +62 -0
- package/src/modules/finance-ledger/docs/commands/RevertSoftLock.md +49 -0
- package/src/modules/finance-ledger/docs/commands/UpdateFiscalYear.md +60 -0
- package/src/modules/finance-ledger/docs/commands/UpdateJournalEntry.md +50 -0
- package/src/modules/finance-ledger/docs/commands/UpdateJournalLine.md +61 -0
- package/src/modules/finance-ledger/docs/commands/VerifySubledgerTransfers.md +59 -0
- package/src/modules/finance-ledger/docs/commands/VerifyTrialBalance.md +53 -0
- package/src/modules/finance-ledger/docs/features/accounting-period-management.md +110 -0
- package/src/modules/finance-ledger/docs/features/financial-statement-generation.md +115 -0
- package/src/modules/finance-ledger/docs/features/journal-entry-management.md +138 -0
- package/src/modules/finance-ledger/docs/features/period-end-close.md +102 -0
- package/src/modules/finance-ledger/docs/features/subledger-integration.md +141 -0
- package/src/modules/finance-ledger/docs/features/trial-balance.md +99 -0
- package/src/modules/finance-ledger/docs/features/year-end-close.md +84 -0
- package/src/modules/finance-ledger/docs/models/AccountingPeriod.md +71 -0
- package/src/modules/finance-ledger/docs/models/FinancialStatement.md +76 -0
- package/src/modules/finance-ledger/docs/models/FinancialStatementLineItem.md +41 -0
- package/src/modules/finance-ledger/docs/models/FiscalYear.md +41 -0
- package/src/modules/finance-ledger/docs/models/JournalEntry.md +80 -0
- package/src/modules/finance-ledger/docs/models/JournalLine.md +47 -0
- package/src/modules/finance-ledger/docs/models/PeriodClose.md +83 -0
- package/src/modules/finance-ledger/docs/models/TrialBalance.md +56 -0
- package/src/modules/finance-ledger/docs/models/TrialBalanceLine.md +37 -0
- package/src/modules/finance-ledger/docs/queries/GetAccountingPeriod.md +35 -0
- package/src/modules/finance-ledger/docs/queries/GetFinancialStatement.md +38 -0
- package/src/modules/finance-ledger/docs/queries/GetFiscalYear.md +35 -0
- package/src/modules/finance-ledger/docs/queries/GetJournalEntry.md +37 -0
- package/src/modules/finance-ledger/docs/queries/GetPeriodByDate.md +38 -0
- package/src/modules/finance-ledger/docs/queries/GetPeriodClose.md +36 -0
- package/src/modules/finance-ledger/docs/queries/GetSubledgerTransferStatus.md +45 -0
- package/src/modules/finance-ledger/docs/queries/GetTrialBalance.md +38 -0
- package/src/modules/finance-ledger/docs/queries/ListAccountingPeriods.md +46 -0
- package/src/modules/finance-ledger/docs/queries/ListFinancialStatements.md +46 -0
- package/src/modules/finance-ledger/docs/queries/ListFiscalYears.md +42 -0
- package/src/modules/finance-ledger/docs/queries/ListJournalEntries.md +48 -0
- package/src/modules/finance-ledger/docs/queries/ListPeriodCloses.md +46 -0
- package/src/modules/finance-ledger/docs/queries/ListTrialBalances.md +51 -0
- package/src/modules/finance-ledger/executor/.gitkeep +0 -0
- package/src/modules/finance-ledger/generated/enums.ts +109 -0
- package/src/modules/finance-ledger/generated/kysely-tailordb.ts +202 -0
- package/src/modules/finance-ledger/index.ts +2 -0
- package/src/modules/finance-ledger/lib/_db_deps.ts +56 -0
- package/src/modules/finance-ledger/lib/errors.generated.ts +332 -0
- package/src/modules/finance-ledger/lib/permissions.generated.ts +41 -0
- package/src/modules/finance-ledger/lib/types.ts +66 -0
- package/src/modules/finance-ledger/module.ts +262 -0
- package/src/modules/finance-ledger/package.json +26 -0
- package/src/modules/finance-ledger/permissions.ts +3 -0
- package/src/modules/finance-ledger/query/.gitkeep +0 -0
- package/src/modules/finance-ledger/query/getAccountingPeriod.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getAccountingPeriod.test.ts +31 -0
- package/src/modules/finance-ledger/query/getAccountingPeriod.ts +21 -0
- package/src/modules/finance-ledger/query/getFinancialStatement.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getFinancialStatement.test.ts +35 -0
- package/src/modules/finance-ledger/query/getFinancialStatement.ts +29 -0
- package/src/modules/finance-ledger/query/getFiscalYear.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getFiscalYear.test.ts +31 -0
- package/src/modules/finance-ledger/query/getFiscalYear.ts +21 -0
- package/src/modules/finance-ledger/query/getJournalEntry.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getJournalEntry.test.ts +35 -0
- package/src/modules/finance-ledger/query/getJournalEntry.ts +29 -0
- package/src/modules/finance-ledger/query/getPeriodByDate.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getPeriodByDate.test.ts +53 -0
- package/src/modules/finance-ledger/query/getPeriodByDate.ts +27 -0
- package/src/modules/finance-ledger/query/getPeriodClose.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getPeriodClose.test.ts +31 -0
- package/src/modules/finance-ledger/query/getPeriodClose.ts +21 -0
- package/src/modules/finance-ledger/query/getSubledgerTransferStatus.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getSubledgerTransferStatus.test.ts +101 -0
- package/src/modules/finance-ledger/query/getSubledgerTransferStatus.ts +68 -0
- package/src/modules/finance-ledger/query/getTrialBalance.generated.ts +5 -0
- package/src/modules/finance-ledger/query/getTrialBalance.test.ts +33 -0
- package/src/modules/finance-ledger/query/getTrialBalance.ts +30 -0
- package/src/modules/finance-ledger/query/listAccountingPeriods.generated.ts +5 -0
- package/src/modules/finance-ledger/query/listAccountingPeriods.test.ts +81 -0
- package/src/modules/finance-ledger/query/listAccountingPeriods.ts +61 -0
- package/src/modules/finance-ledger/query/listFinancialStatements.generated.ts +5 -0
- package/src/modules/finance-ledger/query/listFinancialStatements.test.ts +76 -0
- package/src/modules/finance-ledger/query/listFinancialStatements.ts +62 -0
- package/src/modules/finance-ledger/query/listFiscalYears.generated.ts +5 -0
- package/src/modules/finance-ledger/query/listFiscalYears.test.ts +63 -0
- package/src/modules/finance-ledger/query/listFiscalYears.ts +45 -0
- package/src/modules/finance-ledger/query/listJournalEntries.generated.ts +5 -0
- package/src/modules/finance-ledger/query/listJournalEntries.test.ts +91 -0
- package/src/modules/finance-ledger/query/listJournalEntries.ts +64 -0
- package/src/modules/finance-ledger/query/listPeriodCloses.generated.ts +5 -0
- package/src/modules/finance-ledger/query/listPeriodCloses.test.ts +63 -0
- package/src/modules/finance-ledger/query/listPeriodCloses.ts +64 -0
- package/src/modules/finance-ledger/query/listTrialBalances.generated.ts +5 -0
- package/src/modules/finance-ledger/query/listTrialBalances.test.ts +78 -0
- package/src/modules/finance-ledger/query/listTrialBalances.ts +56 -0
- package/src/modules/finance-ledger/seed/index.ts +19 -0
- package/src/modules/finance-ledger/tailor.config.ts +13 -0
- package/src/modules/finance-ledger/tailor.d.ts +13 -0
- package/src/modules/finance-ledger/testing/commandTestUtils.ts +35 -0
- package/src/modules/finance-ledger/testing/fixtures.ts +382 -0
- package/src/modules/finance-ledger/tsconfig.json +16 -0
- package/src/progress/schema.test.ts +161 -0
- package/src/progress/schema.ts +316 -0
- package/templates/scaffold/app/backend/package.json +1 -3
- package/templates/scaffold/app/backend/vitest.config.ts +4 -21
- package/src/generator/generate-stubs.ts +0 -35
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createMockDb } from "../../../testing/index";
|
|
3
|
+
import type { Transaction } from "../generated/kysely-tailordb";
|
|
4
|
+
import {
|
|
5
|
+
JournalEntryNotFoundError,
|
|
6
|
+
InvalidStatusForPostingError,
|
|
7
|
+
MinimumLinesNotMetError,
|
|
8
|
+
UnbalancedEntryError,
|
|
9
|
+
InvalidPeriodStatusError,
|
|
10
|
+
InactiveAccountError,
|
|
11
|
+
SuccessorAccountInvalidError,
|
|
12
|
+
} from "../lib/errors.generated";
|
|
13
|
+
import {
|
|
14
|
+
baseJournalEntry,
|
|
15
|
+
postedJournalEntry,
|
|
16
|
+
reversedJournalEntry,
|
|
17
|
+
baseJournalLine,
|
|
18
|
+
baseJournalLine2,
|
|
19
|
+
baseAccountingPeriod,
|
|
20
|
+
futureEnterablePeriod,
|
|
21
|
+
neverOpenedPeriod,
|
|
22
|
+
closedPeriod,
|
|
23
|
+
permanentlyClosedPeriod,
|
|
24
|
+
baseAccount,
|
|
25
|
+
baseAccount2,
|
|
26
|
+
inactiveAccount,
|
|
27
|
+
deprecatedAccount,
|
|
28
|
+
} from "../testing/fixtures";
|
|
29
|
+
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
|
|
30
|
+
import { run } from "./postJournalEntry";
|
|
31
|
+
|
|
32
|
+
describe("postJournalEntry", () => {
|
|
33
|
+
it("returns error when journal entry does not exist", async () => {
|
|
34
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
35
|
+
spies.select.mockReturnValueOnce(undefined); // journal entry lookup
|
|
36
|
+
|
|
37
|
+
const result = await run(db, { id: "nonexistent" }, commandCtx);
|
|
38
|
+
|
|
39
|
+
expectErr(result, JournalEntryNotFoundError);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("returns error when journal entry is already POSTED", async () => {
|
|
43
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
44
|
+
spies.select.mockReturnValueOnce(postedJournalEntry);
|
|
45
|
+
|
|
46
|
+
const result = await run(db, { id: postedJournalEntry.id }, commandCtx);
|
|
47
|
+
|
|
48
|
+
expectErr(result, InvalidStatusForPostingError);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("returns error when journal entry is REVERSED", async () => {
|
|
52
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
53
|
+
spies.select.mockReturnValueOnce(reversedJournalEntry);
|
|
54
|
+
|
|
55
|
+
const result = await run(db, { id: reversedJournalEntry.id }, commandCtx);
|
|
56
|
+
|
|
57
|
+
expectErr(result, InvalidStatusForPostingError);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns error when entry has fewer than two journal lines", async () => {
|
|
61
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
62
|
+
spies.select
|
|
63
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
64
|
+
.mockReturnValueOnce(undefined); // lines query returns via execute()
|
|
65
|
+
// execute() for lines returns an array with only one line
|
|
66
|
+
spies.select.mockReset();
|
|
67
|
+
spies.select
|
|
68
|
+
.mockReturnValueOnce(baseJournalEntry)
|
|
69
|
+
.mockImplementationOnce(() => [baseJournalLine]); // journal lines
|
|
70
|
+
|
|
71
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
72
|
+
|
|
73
|
+
expectErr(result, MinimumLinesNotMetError);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("returns error when entry has only one line (debit only or credit only)", async () => {
|
|
77
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
78
|
+
const singleLine = { ...baseJournalLine };
|
|
79
|
+
spies.select
|
|
80
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
81
|
+
.mockImplementationOnce(() => [singleLine]); // only one journal line
|
|
82
|
+
|
|
83
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
84
|
+
|
|
85
|
+
expectErr(result, MinimumLinesNotMetError);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("returns error when total debits do not equal total credits in functional currency", async () => {
|
|
89
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
90
|
+
const unbalancedCreditLine = {
|
|
91
|
+
...baseJournalLine2,
|
|
92
|
+
functionalCreditAmount: 500, // doesn't match debit of 1000
|
|
93
|
+
};
|
|
94
|
+
spies.select
|
|
95
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
96
|
+
.mockImplementationOnce(() => [baseJournalLine, unbalancedCreditLine]) // journal lines
|
|
97
|
+
.mockReturnValueOnce(baseAccountingPeriod); // period lookup
|
|
98
|
+
|
|
99
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
100
|
+
|
|
101
|
+
expectErr(result, UnbalancedEntryError);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("returns error when target period is in NEVER_OPENED status", async () => {
|
|
105
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
106
|
+
const entryInNeverOpened = {
|
|
107
|
+
...baseJournalEntry,
|
|
108
|
+
accountingPeriodId: neverOpenedPeriod.id,
|
|
109
|
+
};
|
|
110
|
+
spies.select
|
|
111
|
+
.mockReturnValueOnce(entryInNeverOpened) // journal entry lookup
|
|
112
|
+
.mockImplementationOnce(() => [baseJournalLine, baseJournalLine2]) // journal lines
|
|
113
|
+
.mockReturnValueOnce(neverOpenedPeriod); // period lookup
|
|
114
|
+
|
|
115
|
+
const result = await run(db, { id: entryInNeverOpened.id }, commandCtx);
|
|
116
|
+
|
|
117
|
+
expectErr(result, InvalidPeriodStatusError);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("returns error when target period is in CLOSED status", async () => {
|
|
121
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
122
|
+
const entryInClosed = {
|
|
123
|
+
...baseJournalEntry,
|
|
124
|
+
accountingPeriodId: closedPeriod.id,
|
|
125
|
+
};
|
|
126
|
+
spies.select
|
|
127
|
+
.mockReturnValueOnce(entryInClosed) // journal entry lookup
|
|
128
|
+
.mockImplementationOnce(() => [baseJournalLine, baseJournalLine2]) // journal lines
|
|
129
|
+
.mockReturnValueOnce(closedPeriod); // period lookup
|
|
130
|
+
|
|
131
|
+
const result = await run(db, { id: entryInClosed.id }, commandCtx);
|
|
132
|
+
|
|
133
|
+
expectErr(result, InvalidPeriodStatusError);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("returns error when target period is in PERMANENTLY_CLOSED status", async () => {
|
|
137
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
138
|
+
const entryInPermClosed = {
|
|
139
|
+
...baseJournalEntry,
|
|
140
|
+
accountingPeriodId: permanentlyClosedPeriod.id,
|
|
141
|
+
};
|
|
142
|
+
spies.select
|
|
143
|
+
.mockReturnValueOnce(entryInPermClosed) // journal entry lookup
|
|
144
|
+
.mockImplementationOnce(() => [baseJournalLine, baseJournalLine2]) // journal lines
|
|
145
|
+
.mockReturnValueOnce(permanentlyClosedPeriod); // period lookup
|
|
146
|
+
|
|
147
|
+
const result = await run(db, { id: entryInPermClosed.id }, commandCtx);
|
|
148
|
+
|
|
149
|
+
expectErr(result, InvalidPeriodStatusError);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("returns error when a GL account is inactive", async () => {
|
|
153
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
154
|
+
const lineWithInactiveAccount = {
|
|
155
|
+
...baseJournalLine,
|
|
156
|
+
accountId: inactiveAccount.id,
|
|
157
|
+
};
|
|
158
|
+
spies.select
|
|
159
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
160
|
+
.mockImplementationOnce(() => [lineWithInactiveAccount, baseJournalLine2]) // journal lines
|
|
161
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup
|
|
162
|
+
.mockReturnValueOnce(inactiveAccount) // account lookup for inactive account
|
|
163
|
+
.mockReturnValueOnce(baseAccount2); // account lookup for active account
|
|
164
|
+
|
|
165
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
166
|
+
|
|
167
|
+
expectErr(result, InactiveAccountError);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("returns error when a GL account is in draft status", async () => {
|
|
171
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
172
|
+
const draftAccount = { ...baseAccount, id: "account-draft", status: "DRAFT" };
|
|
173
|
+
const lineWithDraftAccount = {
|
|
174
|
+
...baseJournalLine,
|
|
175
|
+
accountId: draftAccount.id,
|
|
176
|
+
};
|
|
177
|
+
spies.select
|
|
178
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
179
|
+
.mockImplementationOnce(() => [lineWithDraftAccount, baseJournalLine2]) // journal lines
|
|
180
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup
|
|
181
|
+
.mockReturnValueOnce(draftAccount) // account lookup for draft account
|
|
182
|
+
.mockReturnValueOnce(baseAccount2); // account lookup for active account
|
|
183
|
+
|
|
184
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
185
|
+
|
|
186
|
+
expectErr(result, InactiveAccountError);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("returns error when successor account is itself inactive without further successor", async () => {
|
|
190
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
191
|
+
const deprecatedWithInactiveSuccessor = {
|
|
192
|
+
...deprecatedAccount,
|
|
193
|
+
successorAccountId: inactiveAccount.id,
|
|
194
|
+
};
|
|
195
|
+
const inactiveSuccessor = { ...inactiveAccount, successorAccountId: null };
|
|
196
|
+
const lineWithDeprecatedAccount = {
|
|
197
|
+
...baseJournalLine,
|
|
198
|
+
accountId: deprecatedWithInactiveSuccessor.id,
|
|
199
|
+
};
|
|
200
|
+
spies.select
|
|
201
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
202
|
+
.mockImplementationOnce(() => [lineWithDeprecatedAccount, baseJournalLine2]) // journal lines
|
|
203
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup
|
|
204
|
+
.mockReturnValueOnce(deprecatedWithInactiveSuccessor) // deprecated account lookup
|
|
205
|
+
.mockReturnValueOnce(inactiveSuccessor) // successor account lookup (inactive)
|
|
206
|
+
.mockReturnValueOnce(baseAccount2); // second line account lookup
|
|
207
|
+
|
|
208
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
209
|
+
|
|
210
|
+
expectErr(result, SuccessorAccountInvalidError);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("posts balanced DRAFT entry to OPEN period", async () => {
|
|
214
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
215
|
+
const postedEntry = { ...baseJournalEntry, status: "POSTED", postedAt: new Date() };
|
|
216
|
+
spies.select
|
|
217
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
218
|
+
.mockImplementationOnce(() => [baseJournalLine, baseJournalLine2]) // journal lines
|
|
219
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup (OPEN)
|
|
220
|
+
.mockReturnValueOnce(baseAccount) // account lookup for line 1
|
|
221
|
+
.mockReturnValueOnce(baseAccount2); // account lookup for line 2
|
|
222
|
+
spies.update.mockReturnValueOnce(postedEntry);
|
|
223
|
+
|
|
224
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
225
|
+
|
|
226
|
+
const value = expectOk(result);
|
|
227
|
+
expect(value.journalEntry.status).toBe("POSTED");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("posts balanced DRAFT entry to FUTURE_ENTERABLE period", async () => {
|
|
231
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
232
|
+
const entryInFuture = {
|
|
233
|
+
...baseJournalEntry,
|
|
234
|
+
accountingPeriodId: futureEnterablePeriod.id,
|
|
235
|
+
};
|
|
236
|
+
const postedEntry = { ...entryInFuture, status: "POSTED", postedAt: new Date() };
|
|
237
|
+
spies.select
|
|
238
|
+
.mockReturnValueOnce(entryInFuture) // journal entry lookup
|
|
239
|
+
.mockImplementationOnce(() => [baseJournalLine, baseJournalLine2]) // journal lines
|
|
240
|
+
.mockReturnValueOnce(futureEnterablePeriod) // period lookup (FUTURE_ENTERABLE)
|
|
241
|
+
.mockReturnValueOnce(baseAccount) // account lookup for line 1
|
|
242
|
+
.mockReturnValueOnce(baseAccount2); // account lookup for line 2
|
|
243
|
+
spies.update.mockReturnValueOnce(postedEntry);
|
|
244
|
+
|
|
245
|
+
const result = await run(db, { id: entryInFuture.id }, commandCtx);
|
|
246
|
+
|
|
247
|
+
const value = expectOk(result);
|
|
248
|
+
expect(value.journalEntry.status).toBe("POSTED");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("redirects deprecated GL account to successor account at posting time", async () => {
|
|
252
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
253
|
+
const lineWithDeprecatedAccount = {
|
|
254
|
+
...baseJournalLine,
|
|
255
|
+
accountId: deprecatedAccount.id,
|
|
256
|
+
};
|
|
257
|
+
const postedEntry = { ...baseJournalEntry, status: "POSTED", postedAt: new Date() };
|
|
258
|
+
const updatedLine = { ...lineWithDeprecatedAccount, accountId: baseAccount.id };
|
|
259
|
+
spies.select
|
|
260
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
261
|
+
.mockImplementationOnce(() => [lineWithDeprecatedAccount, baseJournalLine2]) // journal lines
|
|
262
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup
|
|
263
|
+
.mockReturnValueOnce(deprecatedAccount) // deprecated account lookup
|
|
264
|
+
.mockReturnValueOnce(baseAccount) // successor account lookup (ACTIVE)
|
|
265
|
+
.mockReturnValueOnce(baseAccount2); // second line account lookup
|
|
266
|
+
spies.update
|
|
267
|
+
.mockReturnValueOnce(updatedLine) // journal line update (redirect)
|
|
268
|
+
.mockReturnValueOnce(postedEntry); // journal entry update
|
|
269
|
+
|
|
270
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
271
|
+
|
|
272
|
+
const value = expectOk(result);
|
|
273
|
+
expect(value.journalEntry.status).toBe("POSTED");
|
|
274
|
+
expect(value.auditTrail).toBeDefined();
|
|
275
|
+
expect(value.auditTrail.length).toBeGreaterThan(0);
|
|
276
|
+
expect(value.auditTrail[0].type).toBe("SUCCESSOR_REDIRECT");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("records successor account redirect in audit trail", async () => {
|
|
280
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
281
|
+
const lineWithDeprecatedAccount = {
|
|
282
|
+
...baseJournalLine,
|
|
283
|
+
accountId: deprecatedAccount.id,
|
|
284
|
+
};
|
|
285
|
+
const postedEntry = { ...baseJournalEntry, status: "POSTED", postedAt: new Date() };
|
|
286
|
+
const updatedLine = { ...lineWithDeprecatedAccount, accountId: baseAccount.id };
|
|
287
|
+
spies.select
|
|
288
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
289
|
+
.mockImplementationOnce(() => [lineWithDeprecatedAccount, baseJournalLine2]) // journal lines
|
|
290
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup
|
|
291
|
+
.mockReturnValueOnce(deprecatedAccount) // deprecated account lookup
|
|
292
|
+
.mockReturnValueOnce(baseAccount) // successor account lookup (ACTIVE)
|
|
293
|
+
.mockReturnValueOnce(baseAccount2); // second line account lookup
|
|
294
|
+
spies.update
|
|
295
|
+
.mockReturnValueOnce(updatedLine) // journal line update (redirect)
|
|
296
|
+
.mockReturnValueOnce(postedEntry); // journal entry update
|
|
297
|
+
|
|
298
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
299
|
+
|
|
300
|
+
const value = expectOk(result);
|
|
301
|
+
expect(value.auditTrail).toContainEqual(
|
|
302
|
+
expect.objectContaining({
|
|
303
|
+
type: "SUCCESSOR_REDIRECT",
|
|
304
|
+
originalAccountId: deprecatedAccount.id,
|
|
305
|
+
successorAccountId: baseAccount.id,
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("entry becomes immutable after posting", async () => {
|
|
311
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
312
|
+
const postedEntry = { ...baseJournalEntry, status: "POSTED", postedAt: new Date() };
|
|
313
|
+
spies.select
|
|
314
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
315
|
+
.mockImplementationOnce(() => [baseJournalLine, baseJournalLine2]) // journal lines
|
|
316
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup
|
|
317
|
+
.mockReturnValueOnce(baseAccount) // account lookup for line 1
|
|
318
|
+
.mockReturnValueOnce(baseAccount2); // account lookup for line 2
|
|
319
|
+
spies.update.mockReturnValueOnce(postedEntry);
|
|
320
|
+
|
|
321
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
322
|
+
|
|
323
|
+
const value = expectOk(result);
|
|
324
|
+
expect(value.journalEntry.status).toBe("POSTED");
|
|
325
|
+
expect(value.journalEntry.postedAt).toBeDefined();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("emits audit event recording status transition from DRAFT to POSTED and posting user", async () => {
|
|
329
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
330
|
+
const postedEntry = { ...baseJournalEntry, status: "POSTED", postedAt: new Date() };
|
|
331
|
+
spies.select
|
|
332
|
+
.mockReturnValueOnce(baseJournalEntry) // journal entry lookup
|
|
333
|
+
.mockImplementationOnce(() => [baseJournalLine, baseJournalLine2]) // journal lines
|
|
334
|
+
.mockReturnValueOnce(baseAccountingPeriod) // period lookup
|
|
335
|
+
.mockReturnValueOnce(baseAccount) // account lookup for line 1
|
|
336
|
+
.mockReturnValueOnce(baseAccount2); // account lookup for line 2
|
|
337
|
+
spies.update.mockReturnValueOnce(postedEntry);
|
|
338
|
+
|
|
339
|
+
const result = await run(db, { id: baseJournalEntry.id }, commandCtx);
|
|
340
|
+
|
|
341
|
+
const value = expectOk(result);
|
|
342
|
+
expect(value.journalEntry.status).toBe("POSTED");
|
|
343
|
+
// Audit event is emitted as part of successful posting
|
|
344
|
+
expect(value.auditTrail).toBeDefined();
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
|
|
2
|
+
import type { Transaction } from "../generated/kysely-tailordb";
|
|
3
|
+
import {
|
|
4
|
+
JournalEntryNotFoundError,
|
|
5
|
+
InvalidStatusForPostingError,
|
|
6
|
+
MinimumLinesNotMetError,
|
|
7
|
+
UnbalancedEntryError,
|
|
8
|
+
InvalidPeriodStatusError,
|
|
9
|
+
InactiveAccountError,
|
|
10
|
+
SuccessorAccountInvalidError,
|
|
11
|
+
} from "../lib/errors.generated";
|
|
12
|
+
|
|
13
|
+
export interface PostJournalEntryInput {
|
|
14
|
+
id: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface AuditEntry {
|
|
18
|
+
type: string;
|
|
19
|
+
originalAccountId?: string;
|
|
20
|
+
successorAccountId?: string;
|
|
21
|
+
journalLineId?: string;
|
|
22
|
+
from?: string;
|
|
23
|
+
to?: string;
|
|
24
|
+
actorId?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const VALID_PERIOD_STATUSES = ["OPEN", "FUTURE_ENTERABLE"];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Function: postJournalEntry
|
|
31
|
+
*
|
|
32
|
+
* Transitions a journal entry from DRAFT to POSTED status, making it an
|
|
33
|
+
* immutable part of the general ledger. Validates accounting constraints
|
|
34
|
+
* including balance, period status, and account status.
|
|
35
|
+
*/
|
|
36
|
+
export async function run(db: Transaction, input: PostJournalEntryInput, ctx: CommandContext) {
|
|
37
|
+
const { id } = input;
|
|
38
|
+
const auditTrail: AuditEntry[] = [];
|
|
39
|
+
|
|
40
|
+
// 1. Find journal entry
|
|
41
|
+
const journalEntry = await db
|
|
42
|
+
.selectFrom("JournalEntry")
|
|
43
|
+
.selectAll()
|
|
44
|
+
.where("id", "=", id)
|
|
45
|
+
.forUpdate()
|
|
46
|
+
.executeTakeFirst();
|
|
47
|
+
|
|
48
|
+
if (!journalEntry) {
|
|
49
|
+
return err(new JournalEntryNotFoundError(id));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 2. Validate status is DRAFT
|
|
53
|
+
if (journalEntry.status !== "DRAFT") {
|
|
54
|
+
return err(new InvalidStatusForPostingError(id));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 3. Fetch journal lines
|
|
58
|
+
const journalLines = await db
|
|
59
|
+
.selectFrom("JournalLine")
|
|
60
|
+
.selectAll()
|
|
61
|
+
.where("journalEntryId", "=", id)
|
|
62
|
+
.execute();
|
|
63
|
+
|
|
64
|
+
// 4. Validate minimum two lines
|
|
65
|
+
if (journalLines.length < 2) {
|
|
66
|
+
return err(new MinimumLinesNotMetError(id));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 5. Fetch the accounting period
|
|
70
|
+
const period = await db
|
|
71
|
+
.selectFrom("AccountingPeriod")
|
|
72
|
+
.selectAll()
|
|
73
|
+
.where("id", "=", journalEntry.accountingPeriodId)
|
|
74
|
+
.executeTakeFirst();
|
|
75
|
+
|
|
76
|
+
// 6. Validate period status
|
|
77
|
+
if (!period || !VALID_PERIOD_STATUSES.includes(period.status)) {
|
|
78
|
+
return err(new InvalidPeriodStatusError(journalEntry.accountingPeriodId));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 7. Validate balance: total functional debits must equal total functional credits
|
|
82
|
+
const totalDebits = journalLines.reduce(
|
|
83
|
+
(sum, line) => sum + (line.functionalDebitAmount ?? 0),
|
|
84
|
+
0,
|
|
85
|
+
);
|
|
86
|
+
const totalCredits = journalLines.reduce(
|
|
87
|
+
(sum, line) => sum + (line.functionalCreditAmount ?? 0),
|
|
88
|
+
0,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (totalDebits !== totalCredits) {
|
|
92
|
+
return err(new UnbalancedEntryError(id));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 8. Validate all GL accounts and handle successor redirects
|
|
96
|
+
for (const line of journalLines) {
|
|
97
|
+
const account = await db
|
|
98
|
+
.selectFrom("Account")
|
|
99
|
+
.selectAll()
|
|
100
|
+
.where("id", "=", line.accountId)
|
|
101
|
+
.executeTakeFirst();
|
|
102
|
+
|
|
103
|
+
if (!account) {
|
|
104
|
+
return err(new InactiveAccountError(line.accountId));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (account.status === "DEPRECATED" && account.successorAccountId) {
|
|
108
|
+
// Resolve successor
|
|
109
|
+
const successor = await db
|
|
110
|
+
.selectFrom("Account")
|
|
111
|
+
.selectAll()
|
|
112
|
+
.where("id", "=", account.successorAccountId)
|
|
113
|
+
.executeTakeFirst();
|
|
114
|
+
|
|
115
|
+
if (successor?.status !== "ACTIVE") {
|
|
116
|
+
return err(new SuccessorAccountInvalidError(account.successorAccountId ?? line.accountId));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Redirect journal line to successor account
|
|
120
|
+
await db
|
|
121
|
+
.updateTable("JournalLine")
|
|
122
|
+
.set({ accountId: successor.id, updatedAt: new Date() })
|
|
123
|
+
.where("id", "=", line.id)
|
|
124
|
+
.returningAll()
|
|
125
|
+
.executeTakeFirst();
|
|
126
|
+
|
|
127
|
+
auditTrail.push({
|
|
128
|
+
type: "SUCCESSOR_REDIRECT",
|
|
129
|
+
originalAccountId: account.id,
|
|
130
|
+
successorAccountId: successor.id,
|
|
131
|
+
journalLineId: line.id,
|
|
132
|
+
});
|
|
133
|
+
} else if (account.status !== "ACTIVE") {
|
|
134
|
+
return err(new InactiveAccountError(line.accountId));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 9. Transition entry to POSTED
|
|
139
|
+
const now = new Date();
|
|
140
|
+
const updatedEntry = await db
|
|
141
|
+
.updateTable("JournalEntry")
|
|
142
|
+
.set({
|
|
143
|
+
status: "POSTED",
|
|
144
|
+
postedAt: now,
|
|
145
|
+
updatedAt: now,
|
|
146
|
+
})
|
|
147
|
+
.where("id", "=", id)
|
|
148
|
+
.returningAll()
|
|
149
|
+
.executeTakeFirst();
|
|
150
|
+
|
|
151
|
+
// 10. Record audit event
|
|
152
|
+
auditTrail.push({
|
|
153
|
+
type: "STATUS_TRANSITION",
|
|
154
|
+
from: "DRAFT",
|
|
155
|
+
to: "POSTED",
|
|
156
|
+
actorId: ctx.actorId,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return ok({ journalEntry: updatedEntry!, auditTrail });
|
|
160
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @generated — do not edit
|
|
2
|
+
import { defineCommand } from "@tailor-platform/erp-kit/module";
|
|
3
|
+
import { permissions } from "../lib/permissions.generated";
|
|
4
|
+
import { run } from "./processInventoryHandoff";
|
|
5
|
+
|
|
6
|
+
export const processInventoryHandoff = defineCommand(permissions.processInventoryHandoff, run);
|