@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,201 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createMockDb } from "../../../testing/index";
|
|
3
|
+
import type { Transaction } from "../generated/kysely-tailordb";
|
|
4
|
+
import {
|
|
5
|
+
PeriodCloseNotFoundError,
|
|
6
|
+
InvalidStatusTransitionError,
|
|
7
|
+
PendingSubledgerTransactionsError,
|
|
8
|
+
} from "../lib/errors.generated";
|
|
9
|
+
import {
|
|
10
|
+
basePeriodClose,
|
|
11
|
+
inProgressPeriodClose,
|
|
12
|
+
subledgersVerifiedPeriodClose,
|
|
13
|
+
baseJournalEntry,
|
|
14
|
+
baseAccountingPeriod,
|
|
15
|
+
} from "../testing/fixtures";
|
|
16
|
+
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
|
|
17
|
+
import { run } from "./verifySubledgerTransfers";
|
|
18
|
+
|
|
19
|
+
describe("verifySubledgerTransfers", () => {
|
|
20
|
+
it("returns error when PeriodClose record does not exist", async () => {
|
|
21
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
22
|
+
spies.select.mockReturnValueOnce(undefined); // PeriodClose lookup
|
|
23
|
+
|
|
24
|
+
const result = await run(db, { id: "nonexistent" }, commandCtx);
|
|
25
|
+
|
|
26
|
+
expectErr(result, PeriodCloseNotFoundError);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("returns error when PeriodClose is in NOT_STARTED status", async () => {
|
|
30
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
31
|
+
spies.select.mockReturnValueOnce(basePeriodClose); // PeriodClose in NOT_STARTED
|
|
32
|
+
|
|
33
|
+
const result = await run(db, { id: basePeriodClose.id }, commandCtx);
|
|
34
|
+
|
|
35
|
+
expectErr(result, InvalidStatusTransitionError);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("returns error when PeriodClose is already in SUBLEDGERS_VERIFIED status", async () => {
|
|
39
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
40
|
+
spies.select.mockReturnValueOnce(subledgersVerifiedPeriodClose); // PeriodClose in SUBLEDGERS_VERIFIED
|
|
41
|
+
|
|
42
|
+
const result = await run(db, { id: subledgersVerifiedPeriodClose.id }, commandCtx);
|
|
43
|
+
|
|
44
|
+
expectErr(result, InvalidStatusTransitionError);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns error when purchase module has pending unposted transactions", async () => {
|
|
48
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
49
|
+
const purchaseDraft = {
|
|
50
|
+
...baseJournalEntry,
|
|
51
|
+
id: "je-purchase-draft",
|
|
52
|
+
journalType: "PURCHASE",
|
|
53
|
+
accountingPeriodId: baseAccountingPeriod.id,
|
|
54
|
+
status: "DRAFT",
|
|
55
|
+
};
|
|
56
|
+
spies.select
|
|
57
|
+
.mockReturnValueOnce(inProgressPeriodClose) // PeriodClose lookup
|
|
58
|
+
.mockReturnValueOnce([purchaseDraft]) // purchase DRAFT entries
|
|
59
|
+
.mockReturnValueOnce([]) // sales: no pending
|
|
60
|
+
.mockReturnValueOnce([]) // inventory: no pending
|
|
61
|
+
.mockReturnValueOnce([]); // manufacturing: no pending
|
|
62
|
+
|
|
63
|
+
const result = await run(db, { id: inProgressPeriodClose.id }, commandCtx);
|
|
64
|
+
|
|
65
|
+
expectErr(result, PendingSubledgerTransactionsError);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("returns error when sales module has pending unposted transactions", async () => {
|
|
69
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
70
|
+
const salesDraft = {
|
|
71
|
+
...baseJournalEntry,
|
|
72
|
+
id: "je-sales-draft",
|
|
73
|
+
journalType: "SALES",
|
|
74
|
+
accountingPeriodId: baseAccountingPeriod.id,
|
|
75
|
+
status: "DRAFT",
|
|
76
|
+
};
|
|
77
|
+
spies.select
|
|
78
|
+
.mockReturnValueOnce(inProgressPeriodClose) // PeriodClose lookup
|
|
79
|
+
.mockReturnValueOnce([]) // purchase: no pending
|
|
80
|
+
.mockReturnValueOnce([salesDraft]) // sales: pending
|
|
81
|
+
.mockReturnValueOnce([]) // inventory: no pending
|
|
82
|
+
.mockReturnValueOnce([]); // manufacturing: no pending
|
|
83
|
+
|
|
84
|
+
const result = await run(db, { id: inProgressPeriodClose.id }, commandCtx);
|
|
85
|
+
|
|
86
|
+
expectErr(result, PendingSubledgerTransactionsError);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("returns error when inventory module has pending unposted transactions", async () => {
|
|
90
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
91
|
+
const inventoryDraft = {
|
|
92
|
+
...baseJournalEntry,
|
|
93
|
+
id: "je-inventory-draft",
|
|
94
|
+
journalType: "MISCELLANEOUS",
|
|
95
|
+
accountingPeriodId: baseAccountingPeriod.id,
|
|
96
|
+
status: "DRAFT",
|
|
97
|
+
};
|
|
98
|
+
spies.select
|
|
99
|
+
.mockReturnValueOnce(inProgressPeriodClose) // PeriodClose lookup
|
|
100
|
+
.mockReturnValueOnce([]) // purchase: no pending
|
|
101
|
+
.mockReturnValueOnce([]) // sales: no pending
|
|
102
|
+
.mockReturnValueOnce([inventoryDraft]) // inventory: pending
|
|
103
|
+
.mockReturnValueOnce([]); // manufacturing: no pending
|
|
104
|
+
|
|
105
|
+
const result = await run(db, { id: inProgressPeriodClose.id }, commandCtx);
|
|
106
|
+
|
|
107
|
+
expectErr(result, PendingSubledgerTransactionsError);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("returns error when manufacturing module has pending unposted transactions", async () => {
|
|
111
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
112
|
+
const manufacturingDraft = {
|
|
113
|
+
...baseJournalEntry,
|
|
114
|
+
id: "je-manufacturing-draft",
|
|
115
|
+
journalType: "MISCELLANEOUS",
|
|
116
|
+
accountingPeriodId: baseAccountingPeriod.id,
|
|
117
|
+
status: "DRAFT",
|
|
118
|
+
};
|
|
119
|
+
spies.select
|
|
120
|
+
.mockReturnValueOnce(inProgressPeriodClose) // PeriodClose lookup
|
|
121
|
+
.mockReturnValueOnce([]) // purchase: no pending
|
|
122
|
+
.mockReturnValueOnce([]) // sales: no pending
|
|
123
|
+
.mockReturnValueOnce([]) // inventory: no pending
|
|
124
|
+
.mockReturnValueOnce([manufacturingDraft]); // manufacturing: pending
|
|
125
|
+
|
|
126
|
+
const result = await run(db, { id: inProgressPeriodClose.id }, commandCtx);
|
|
127
|
+
|
|
128
|
+
expectErr(result, PendingSubledgerTransactionsError);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("transitions IN_PROGRESS to SUBLEDGERS_VERIFIED when all subledgers are reconciled", async () => {
|
|
132
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
133
|
+
const updatedPeriodClose = {
|
|
134
|
+
...inProgressPeriodClose,
|
|
135
|
+
status: "SUBLEDGERS_VERIFIED",
|
|
136
|
+
completionPercentage: 25,
|
|
137
|
+
};
|
|
138
|
+
spies.select
|
|
139
|
+
.mockReturnValueOnce(inProgressPeriodClose) // PeriodClose lookup
|
|
140
|
+
.mockReturnValueOnce([]) // purchase: no pending
|
|
141
|
+
.mockReturnValueOnce([]) // sales: no pending
|
|
142
|
+
.mockReturnValueOnce([]) // inventory: no pending
|
|
143
|
+
.mockReturnValueOnce([]); // manufacturing: no pending
|
|
144
|
+
spies.update.mockReturnValueOnce(updatedPeriodClose);
|
|
145
|
+
|
|
146
|
+
const result = await run(db, { id: inProgressPeriodClose.id }, commandCtx);
|
|
147
|
+
|
|
148
|
+
const value = expectOk(result);
|
|
149
|
+
expect(value.periodClose.status).toBe("SUBLEDGERS_VERIFIED");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("updates completionPercentage after successful verification", async () => {
|
|
153
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
154
|
+
const updatedPeriodClose = {
|
|
155
|
+
...inProgressPeriodClose,
|
|
156
|
+
status: "SUBLEDGERS_VERIFIED",
|
|
157
|
+
completionPercentage: 25,
|
|
158
|
+
};
|
|
159
|
+
spies.select
|
|
160
|
+
.mockReturnValueOnce(inProgressPeriodClose) // PeriodClose lookup
|
|
161
|
+
.mockReturnValueOnce([]) // purchase: no pending
|
|
162
|
+
.mockReturnValueOnce([]) // sales: no pending
|
|
163
|
+
.mockReturnValueOnce([]) // inventory: no pending
|
|
164
|
+
.mockReturnValueOnce([]); // manufacturing: no pending
|
|
165
|
+
spies.update.mockReturnValueOnce(updatedPeriodClose);
|
|
166
|
+
|
|
167
|
+
const result = await run(db, { id: inProgressPeriodClose.id }, commandCtx);
|
|
168
|
+
|
|
169
|
+
const value = expectOk(result);
|
|
170
|
+
expect(value.periodClose.completionPercentage).toBe(25);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("emits audit event recording subledger verification results and status transition", async () => {
|
|
174
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
175
|
+
const updatedPeriodClose = {
|
|
176
|
+
...inProgressPeriodClose,
|
|
177
|
+
status: "SUBLEDGERS_VERIFIED",
|
|
178
|
+
completionPercentage: 25,
|
|
179
|
+
};
|
|
180
|
+
spies.select
|
|
181
|
+
.mockReturnValueOnce(inProgressPeriodClose) // PeriodClose lookup
|
|
182
|
+
.mockReturnValueOnce([]) // purchase: no pending
|
|
183
|
+
.mockReturnValueOnce([]) // sales: no pending
|
|
184
|
+
.mockReturnValueOnce([]) // inventory: no pending
|
|
185
|
+
.mockReturnValueOnce([]); // manufacturing: no pending
|
|
186
|
+
spies.update.mockReturnValueOnce(updatedPeriodClose);
|
|
187
|
+
|
|
188
|
+
const result = await run(db, { id: inProgressPeriodClose.id }, commandCtx);
|
|
189
|
+
|
|
190
|
+
const value = expectOk(result);
|
|
191
|
+
expect(value.auditEvent).toBeDefined();
|
|
192
|
+
expect(value.auditEvent.type).toBe("SUBLEDGER_VERIFICATION_COMPLETED");
|
|
193
|
+
expect(value.auditEvent.actorId).toBe(commandCtx.actorId);
|
|
194
|
+
expect(value.auditEvent.from).toBe("IN_PROGRESS");
|
|
195
|
+
expect(value.auditEvent.to).toBe("SUBLEDGERS_VERIFIED");
|
|
196
|
+
expect(value.auditEvent.verificationResults).toHaveLength(4);
|
|
197
|
+
expect(
|
|
198
|
+
value.auditEvent.verificationResults.every((r: { reconciled: boolean }) => r.reconciled),
|
|
199
|
+
).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
|
|
2
|
+
import type { Transaction } from "../generated/kysely-tailordb";
|
|
3
|
+
import {
|
|
4
|
+
PeriodCloseNotFoundError,
|
|
5
|
+
InvalidStatusTransitionError,
|
|
6
|
+
PendingSubledgerTransactionsError,
|
|
7
|
+
} from "../lib/errors.generated";
|
|
8
|
+
|
|
9
|
+
export interface VerifySubledgerTransfersInput {
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SubledgerVerificationResult {
|
|
14
|
+
module: string;
|
|
15
|
+
journalType: string;
|
|
16
|
+
pendingCount: number;
|
|
17
|
+
reconciled: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const SUBLEDGER_MODULES: { module: string; journalType: string }[] = [
|
|
21
|
+
{ module: "purchase", journalType: "PURCHASE" },
|
|
22
|
+
{ module: "sales", journalType: "SALES" },
|
|
23
|
+
{ module: "inventory", journalType: "MISCELLANEOUS" },
|
|
24
|
+
{ module: "manufacturing", journalType: "MISCELLANEOUS" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const SUBLEDGERS_VERIFIED_COMPLETION_PERCENTAGE = 25;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Function: verifySubledgerTransfers
|
|
31
|
+
*
|
|
32
|
+
* Checks that all registered subledger modules have completed their
|
|
33
|
+
* transaction transfers to the general ledger for the closing period.
|
|
34
|
+
* If all subledgers are reconciled, the PeriodClose transitions from
|
|
35
|
+
* IN_PROGRESS to SUBLEDGERS_VERIFIED.
|
|
36
|
+
*/
|
|
37
|
+
export async function run(
|
|
38
|
+
db: Transaction,
|
|
39
|
+
input: VerifySubledgerTransfersInput,
|
|
40
|
+
ctx: CommandContext,
|
|
41
|
+
) {
|
|
42
|
+
const { id } = input;
|
|
43
|
+
|
|
44
|
+
// 1. Find PeriodClose record
|
|
45
|
+
const periodClose = await db
|
|
46
|
+
.selectFrom("PeriodClose")
|
|
47
|
+
.selectAll()
|
|
48
|
+
.where("id", "=", id)
|
|
49
|
+
.forUpdate()
|
|
50
|
+
.executeTakeFirst();
|
|
51
|
+
|
|
52
|
+
if (!periodClose) {
|
|
53
|
+
return err(new PeriodCloseNotFoundError(id));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 2. Validate PeriodClose is in IN_PROGRESS status
|
|
57
|
+
if (periodClose.status !== "IN_PROGRESS") {
|
|
58
|
+
return err(new InvalidStatusTransitionError(id));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Check all registered subledger modules for pending unposted transactions
|
|
62
|
+
const verificationResults: SubledgerVerificationResult[] = [];
|
|
63
|
+
|
|
64
|
+
for (const subledger of SUBLEDGER_MODULES) {
|
|
65
|
+
const pendingEntries = await db
|
|
66
|
+
.selectFrom("JournalEntry")
|
|
67
|
+
.selectAll()
|
|
68
|
+
.where("accountingPeriodId", "=", periodClose.accountingPeriodId)
|
|
69
|
+
.where("journalType", "=", subledger.journalType as "PURCHASE" | "SALES" | "MISCELLANEOUS")
|
|
70
|
+
.where("status", "=", "DRAFT")
|
|
71
|
+
.execute();
|
|
72
|
+
|
|
73
|
+
verificationResults.push({
|
|
74
|
+
module: subledger.module,
|
|
75
|
+
journalType: subledger.journalType,
|
|
76
|
+
pendingCount: pendingEntries.length,
|
|
77
|
+
reconciled: pendingEntries.length === 0,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 4. If any subledger has pending transactions, fail
|
|
82
|
+
const pendingModules = verificationResults.filter((r) => !r.reconciled);
|
|
83
|
+
if (pendingModules.length > 0) {
|
|
84
|
+
return err(new PendingSubledgerTransactionsError(id));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 5. Transition PeriodClose to SUBLEDGERS_VERIFIED and update completionPercentage
|
|
88
|
+
const now = new Date();
|
|
89
|
+
const updatedPeriodClose = await db
|
|
90
|
+
.updateTable("PeriodClose")
|
|
91
|
+
.set({
|
|
92
|
+
status: "SUBLEDGERS_VERIFIED",
|
|
93
|
+
completionPercentage: SUBLEDGERS_VERIFIED_COMPLETION_PERCENTAGE,
|
|
94
|
+
subledgerVerificationDetails: JSON.stringify(verificationResults),
|
|
95
|
+
updatedAt: now,
|
|
96
|
+
})
|
|
97
|
+
.where("id", "=", id)
|
|
98
|
+
.returningAll()
|
|
99
|
+
.executeTakeFirst();
|
|
100
|
+
|
|
101
|
+
// 6. Emit audit event
|
|
102
|
+
const auditEvent = {
|
|
103
|
+
type: "SUBLEDGER_VERIFICATION_COMPLETED",
|
|
104
|
+
actorId: ctx.actorId,
|
|
105
|
+
timestamp: now,
|
|
106
|
+
periodCloseId: id,
|
|
107
|
+
from: "IN_PROGRESS",
|
|
108
|
+
to: "SUBLEDGERS_VERIFIED",
|
|
109
|
+
verificationResults,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return ok({ periodClose: updatedPeriodClose!, auditEvent });
|
|
113
|
+
}
|
|
@@ -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 "./verifyTrialBalance";
|
|
5
|
+
|
|
6
|
+
export const verifyTrialBalance = defineCommand(permissions.verifyTrialBalance, run);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createMockDb } from "../../../testing/index";
|
|
3
|
+
import type { Transaction } from "../generated/kysely-tailordb";
|
|
4
|
+
import {
|
|
5
|
+
PeriodCloseNotFoundError,
|
|
6
|
+
InvalidStatusTransitionError,
|
|
7
|
+
TrialBalanceImbalancedError,
|
|
8
|
+
} from "../lib/errors.generated";
|
|
9
|
+
import {
|
|
10
|
+
adjustmentsPostedPeriodClose,
|
|
11
|
+
subledgersVerifiedPeriodClose,
|
|
12
|
+
trialBalanceVerifiedPeriodClose,
|
|
13
|
+
baseTrialBalance,
|
|
14
|
+
baseAccountingPeriod,
|
|
15
|
+
baseChartOfAccounts,
|
|
16
|
+
} from "../testing/fixtures";
|
|
17
|
+
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
|
|
18
|
+
import { run } from "./verifyTrialBalance";
|
|
19
|
+
|
|
20
|
+
describe("verifyTrialBalance", () => {
|
|
21
|
+
it("returns error when PeriodClose record does not exist", async () => {
|
|
22
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
23
|
+
spies.select.mockReturnValueOnce(undefined); // PeriodClose lookup
|
|
24
|
+
|
|
25
|
+
const result = await run(db, { id: "nonexistent" }, commandCtx);
|
|
26
|
+
|
|
27
|
+
expectErr(result, PeriodCloseNotFoundError);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("returns error when PeriodClose is in SUBLEDGERS_VERIFIED status", async () => {
|
|
31
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
32
|
+
spies.select.mockReturnValueOnce(subledgersVerifiedPeriodClose); // PeriodClose lookup
|
|
33
|
+
|
|
34
|
+
const result = await run(db, { id: subledgersVerifiedPeriodClose.id }, commandCtx);
|
|
35
|
+
|
|
36
|
+
expectErr(result, InvalidStatusTransitionError);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("returns error when PeriodClose is already in TRIAL_BALANCE_VERIFIED status", async () => {
|
|
40
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
41
|
+
spies.select.mockReturnValueOnce(trialBalanceVerifiedPeriodClose); // PeriodClose lookup
|
|
42
|
+
|
|
43
|
+
const result = await run(db, { id: trialBalanceVerifiedPeriodClose.id }, commandCtx);
|
|
44
|
+
|
|
45
|
+
expectErr(result, InvalidStatusTransitionError);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("returns error when trial balance has a non-zero debit-credit discrepancy", async () => {
|
|
49
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
50
|
+
const imbalancedTrialBalance = {
|
|
51
|
+
...baseTrialBalance,
|
|
52
|
+
id: "trial-balance-imbalanced",
|
|
53
|
+
totalDebits: 10000,
|
|
54
|
+
totalCredits: 9500,
|
|
55
|
+
isBalanced: false,
|
|
56
|
+
};
|
|
57
|
+
spies.select
|
|
58
|
+
.mockReturnValueOnce(adjustmentsPostedPeriodClose) // PeriodClose lookup
|
|
59
|
+
.mockReturnValueOnce(baseAccountingPeriod) // AccountingPeriod lookup
|
|
60
|
+
.mockReturnValueOnce(baseChartOfAccounts) // ChartOfAccounts lookup
|
|
61
|
+
.mockReturnValueOnce(imbalancedTrialBalance); // TrialBalance lookup
|
|
62
|
+
|
|
63
|
+
const result = await run(db, { id: adjustmentsPostedPeriodClose.id }, commandCtx);
|
|
64
|
+
|
|
65
|
+
expectErr(result, TrialBalanceImbalancedError);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("transitions ADJUSTMENTS_POSTED to TRIAL_BALANCE_VERIFIED when trial balance is balanced", async () => {
|
|
69
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
70
|
+
const updatedPeriodClose = {
|
|
71
|
+
...adjustmentsPostedPeriodClose,
|
|
72
|
+
status: "TRIAL_BALANCE_VERIFIED",
|
|
73
|
+
trialBalanceId: baseTrialBalance.id,
|
|
74
|
+
completionPercentage: 62,
|
|
75
|
+
};
|
|
76
|
+
spies.select
|
|
77
|
+
.mockReturnValueOnce(adjustmentsPostedPeriodClose) // PeriodClose lookup
|
|
78
|
+
.mockReturnValueOnce(baseAccountingPeriod) // AccountingPeriod lookup
|
|
79
|
+
.mockReturnValueOnce(baseChartOfAccounts) // ChartOfAccounts lookup
|
|
80
|
+
.mockReturnValueOnce(baseTrialBalance); // TrialBalance lookup (balanced)
|
|
81
|
+
spies.update.mockReturnValueOnce(updatedPeriodClose);
|
|
82
|
+
|
|
83
|
+
const result = await run(db, { id: adjustmentsPostedPeriodClose.id }, commandCtx);
|
|
84
|
+
|
|
85
|
+
const value = expectOk(result);
|
|
86
|
+
expect(value.periodClose.status).toBe("TRIAL_BALANCE_VERIFIED");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("updates completionPercentage after successful verification", async () => {
|
|
90
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
91
|
+
const updatedPeriodClose = {
|
|
92
|
+
...adjustmentsPostedPeriodClose,
|
|
93
|
+
status: "TRIAL_BALANCE_VERIFIED",
|
|
94
|
+
trialBalanceId: baseTrialBalance.id,
|
|
95
|
+
completionPercentage: 62,
|
|
96
|
+
};
|
|
97
|
+
spies.select
|
|
98
|
+
.mockReturnValueOnce(adjustmentsPostedPeriodClose) // PeriodClose lookup
|
|
99
|
+
.mockReturnValueOnce(baseAccountingPeriod) // AccountingPeriod lookup
|
|
100
|
+
.mockReturnValueOnce(baseChartOfAccounts) // ChartOfAccounts lookup
|
|
101
|
+
.mockReturnValueOnce(baseTrialBalance); // TrialBalance lookup (balanced)
|
|
102
|
+
spies.update.mockReturnValueOnce(updatedPeriodClose);
|
|
103
|
+
|
|
104
|
+
const result = await run(db, { id: adjustmentsPostedPeriodClose.id }, commandCtx);
|
|
105
|
+
|
|
106
|
+
const value = expectOk(result);
|
|
107
|
+
expect(value.periodClose.completionPercentage).toBe(62);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("emits audit event recording trial balance result and status transition", async () => {
|
|
111
|
+
const { db, spies } = createMockDb<Transaction>();
|
|
112
|
+
const updatedPeriodClose = {
|
|
113
|
+
...adjustmentsPostedPeriodClose,
|
|
114
|
+
status: "TRIAL_BALANCE_VERIFIED",
|
|
115
|
+
trialBalanceId: baseTrialBalance.id,
|
|
116
|
+
completionPercentage: 62,
|
|
117
|
+
};
|
|
118
|
+
spies.select
|
|
119
|
+
.mockReturnValueOnce(adjustmentsPostedPeriodClose) // PeriodClose lookup
|
|
120
|
+
.mockReturnValueOnce(baseAccountingPeriod) // AccountingPeriod lookup
|
|
121
|
+
.mockReturnValueOnce(baseChartOfAccounts) // ChartOfAccounts lookup
|
|
122
|
+
.mockReturnValueOnce(baseTrialBalance); // TrialBalance lookup (balanced)
|
|
123
|
+
spies.update.mockReturnValueOnce(updatedPeriodClose);
|
|
124
|
+
|
|
125
|
+
const result = await run(db, { id: adjustmentsPostedPeriodClose.id }, commandCtx);
|
|
126
|
+
|
|
127
|
+
const value = expectOk(result);
|
|
128
|
+
expect(value.auditEvent).toBeDefined();
|
|
129
|
+
expect(value.auditEvent.actorId).toBe(commandCtx.actorId);
|
|
130
|
+
expect(value.auditEvent.timestamp).toBeInstanceOf(Date);
|
|
131
|
+
expect(value.auditEvent.from).toBe("ADJUSTMENTS_POSTED");
|
|
132
|
+
expect(value.auditEvent.to).toBe("TRIAL_BALANCE_VERIFIED");
|
|
133
|
+
expect(value.auditEvent.trialBalanceId).toBe(baseTrialBalance.id);
|
|
134
|
+
expect(value.auditEvent.isBalanced).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";
|
|
2
|
+
import type { Transaction } from "../generated/kysely-tailordb";
|
|
3
|
+
import {
|
|
4
|
+
PeriodCloseNotFoundError,
|
|
5
|
+
InvalidStatusTransitionError,
|
|
6
|
+
TrialBalanceImbalancedError,
|
|
7
|
+
} from "../lib/errors.generated";
|
|
8
|
+
|
|
9
|
+
export interface VerifyTrialBalanceInput {
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Function: verifyTrialBalance
|
|
15
|
+
*
|
|
16
|
+
* Checks that the period's trial balance is balanced (total debits equal total
|
|
17
|
+
* credits across all accounts) and transitions the PeriodClose from
|
|
18
|
+
* ADJUSTMENTS_POSTED to TRIAL_BALANCE_VERIFIED status.
|
|
19
|
+
*/
|
|
20
|
+
export async function run(db: Transaction, input: VerifyTrialBalanceInput, ctx: CommandContext) {
|
|
21
|
+
const { id } = input;
|
|
22
|
+
|
|
23
|
+
// 1. Find PeriodClose record
|
|
24
|
+
const periodClose = await db
|
|
25
|
+
.selectFrom("PeriodClose")
|
|
26
|
+
.selectAll()
|
|
27
|
+
.where("id", "=", id)
|
|
28
|
+
.forUpdate()
|
|
29
|
+
.executeTakeFirst();
|
|
30
|
+
|
|
31
|
+
if (!periodClose) {
|
|
32
|
+
return err(new PeriodCloseNotFoundError(id));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 2. Validate status is ADJUSTMENTS_POSTED
|
|
36
|
+
if (periodClose.status !== "ADJUSTMENTS_POSTED") {
|
|
37
|
+
return err(new InvalidStatusTransitionError(id));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 3. Retrieve the accounting period to find the company and period context
|
|
41
|
+
const accountingPeriod = await db
|
|
42
|
+
.selectFrom("AccountingPeriod")
|
|
43
|
+
.selectAll()
|
|
44
|
+
.where("id", "=", periodClose.accountingPeriodId)
|
|
45
|
+
.executeTakeFirst();
|
|
46
|
+
|
|
47
|
+
// 4. Retrieve the active chart of accounts for trial balance generation
|
|
48
|
+
const chartOfAccounts = await db
|
|
49
|
+
.selectFrom("ChartOfAccounts")
|
|
50
|
+
.selectAll()
|
|
51
|
+
.where("companyId", "=", accountingPeriod!.companyId)
|
|
52
|
+
.where("status", "=", "ACTIVE")
|
|
53
|
+
.executeTakeFirst();
|
|
54
|
+
|
|
55
|
+
// 5. Generate or retrieve the trial balance for the period
|
|
56
|
+
const trialBalance = await db
|
|
57
|
+
.selectFrom("TrialBalance")
|
|
58
|
+
.selectAll()
|
|
59
|
+
.where("accountingPeriodId", "=", periodClose.accountingPeriodId)
|
|
60
|
+
.where("chartOfAccountsId", "=", chartOfAccounts!.id)
|
|
61
|
+
.executeTakeFirst();
|
|
62
|
+
|
|
63
|
+
// 6. Verify trial balance is balanced
|
|
64
|
+
if (!trialBalance || trialBalance.totalDebits !== trialBalance.totalCredits) {
|
|
65
|
+
const discrepancy = trialBalance
|
|
66
|
+
? Math.abs(trialBalance.totalDebits - trialBalance.totalCredits)
|
|
67
|
+
: 0;
|
|
68
|
+
return err(new TrialBalanceImbalancedError(`${id} (discrepancy: ${discrepancy})`));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 7. Transition PeriodClose to TRIAL_BALANCE_VERIFIED and update completionPercentage
|
|
72
|
+
const now = new Date();
|
|
73
|
+
const updated = await db
|
|
74
|
+
.updateTable("PeriodClose")
|
|
75
|
+
.set({
|
|
76
|
+
status: "TRIAL_BALANCE_VERIFIED",
|
|
77
|
+
trialBalanceId: trialBalance.id,
|
|
78
|
+
completionPercentage: 62,
|
|
79
|
+
updatedAt: now,
|
|
80
|
+
})
|
|
81
|
+
.where("id", "=", id)
|
|
82
|
+
.returningAll()
|
|
83
|
+
.executeTakeFirstOrThrow();
|
|
84
|
+
|
|
85
|
+
// 8. Emit audit event
|
|
86
|
+
const auditEvent = {
|
|
87
|
+
type: "TRIAL_BALANCE_VERIFIED",
|
|
88
|
+
from: "ADJUSTMENTS_POSTED",
|
|
89
|
+
to: "TRIAL_BALANCE_VERIFIED",
|
|
90
|
+
actorId: ctx.actorId,
|
|
91
|
+
timestamp: now,
|
|
92
|
+
trialBalanceId: trialBalance.id,
|
|
93
|
+
isBalanced: true,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return ok({ periodClose: updated, auditEvent });
|
|
97
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
db,
|
|
3
|
+
type TailorAnyDBField,
|
|
4
|
+
type TailorAnyDBType,
|
|
5
|
+
unsafeAllowAllGqlPermission,
|
|
6
|
+
unsafeAllowAllTypePermission,
|
|
7
|
+
} from "@tailor-platform/sdk";
|
|
8
|
+
import { company as companyStub } from "../lib/_db_deps";
|
|
9
|
+
import { fiscalYear } from "./fiscalYear";
|
|
10
|
+
|
|
11
|
+
export const ACCOUNTING_PERIOD_STATUSES: [string, string, string, string, string] = [
|
|
12
|
+
"NEVER_OPENED",
|
|
13
|
+
"FUTURE_ENTERABLE",
|
|
14
|
+
"OPEN",
|
|
15
|
+
"CLOSED",
|
|
16
|
+
"PERMANENTLY_CLOSED",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export interface CreateAccountingPeriodTypeParams<F extends Record<string, TailorAnyDBField>> {
|
|
20
|
+
fields?: F;
|
|
21
|
+
companyType?: TailorAnyDBType;
|
|
22
|
+
fiscalYearType?: TailorAnyDBType;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createAccountingPeriodType<const F extends Record<string, TailorAnyDBField>>(
|
|
26
|
+
params: CreateAccountingPeriodTypeParams<F>,
|
|
27
|
+
) {
|
|
28
|
+
return db
|
|
29
|
+
.type("AccountingPeriod", {
|
|
30
|
+
companyId: db
|
|
31
|
+
.uuid()
|
|
32
|
+
.relation({
|
|
33
|
+
type: "n-1",
|
|
34
|
+
toward: { type: params.companyType ?? companyStub },
|
|
35
|
+
backward: "accountingPeriods",
|
|
36
|
+
})
|
|
37
|
+
.description("Foreign key to Company"),
|
|
38
|
+
fiscalYearId: db
|
|
39
|
+
.uuid()
|
|
40
|
+
.relation({
|
|
41
|
+
type: "n-1",
|
|
42
|
+
toward: { type: params.fiscalYearType ?? fiscalYear },
|
|
43
|
+
backward: "accountingPeriods",
|
|
44
|
+
})
|
|
45
|
+
.description("Foreign key to FiscalYear"),
|
|
46
|
+
name: db.string().description("Period name"),
|
|
47
|
+
startDate: db.date().description("Period start date"),
|
|
48
|
+
endDate: db.date().description("Period end date"),
|
|
49
|
+
periodType: db.enum(["OPERATING", "ADJUSTMENT"]).description("Period type"),
|
|
50
|
+
status: db.enum(ACCOUNTING_PERIOD_STATUSES).description("Accounting period lifecycle status"),
|
|
51
|
+
...((params.fields ?? {}) as F),
|
|
52
|
+
...db.fields.timestamps(),
|
|
53
|
+
})
|
|
54
|
+
.permission(unsafeAllowAllTypePermission)
|
|
55
|
+
.gqlPermission(unsafeAllowAllGqlPermission);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const accountingPeriod = createAccountingPeriodType({});
|