@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.
Files changed (290) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +10 -10
  3. package/dist/cli.mjs +407 -69
  4. package/package.json +1 -1
  5. package/skills/erp-kit-app-1-requirements/SKILL.md +33 -17
  6. package/skills/erp-kit-app-2-requirements-review/SKILL.md +12 -0
  7. package/skills/erp-kit-app-3-plan/SKILL.md +18 -4
  8. package/skills/erp-kit-app-3-plan/references/resolver-extraction.md +1 -1
  9. package/skills/erp-kit-app-3-plan/references/screen-extraction.md +1 -1
  10. package/skills/erp-kit-app-4-plan-review/SKILL.md +12 -0
  11. package/skills/erp-kit-app-5-impl-backend/SKILL.md +12 -0
  12. package/skills/erp-kit-app-6-impl-frontend/SKILL.md +12 -0
  13. package/skills/erp-kit-app-7-impl-review/SKILL.md +13 -1
  14. package/skills/erp-kit-app-shared/references/progress-protocol.md +77 -0
  15. package/skills/erp-kit-mock-scenario/SKILL.md +1 -1
  16. package/skills/erp-kit-module-1-requirements/SKILL.md +1 -1
  17. package/skills/erp-kit-module-3-plan/SKILL.md +3 -3
  18. package/skills/erp-kit-module-3-update-plan/SKILL.md +3 -3
  19. package/skills/erp-kit-module-5-impl/SKILL.md +1 -1
  20. package/src/commands/app/index.ts +2 -0
  21. package/src/commands/app/progress/git-context.ts +16 -0
  22. package/src/commands/app/progress/index.ts +45 -0
  23. package/src/commands/app/progress/log.ts +49 -0
  24. package/src/commands/app/progress/progress.test.ts +128 -0
  25. package/src/commands/app/progress/schema-cmd.ts +10 -0
  26. package/src/commands/check.test.ts +4 -4
  27. package/src/commands/lib/discovery.test.ts +5 -7
  28. package/src/commands/lib/discovery.ts +8 -8
  29. package/src/commands/lib/sync-check-source.test.ts +1 -1
  30. package/src/commands/lib/sync-check-source.ts +6 -1
  31. package/src/commands/lib/sync-check-tests.test.ts +43 -0
  32. package/src/commands/lib/sync-check-tests.ts +20 -2
  33. package/src/commands/sync-check.ts +3 -0
  34. package/src/generator/generate-app-code.test.ts +0 -6
  35. package/src/generator/generate-app-code.ts +3 -13
  36. package/src/generator/generate-code.test.ts +10 -40
  37. package/src/generator/generate-code.ts +6 -12
  38. package/src/generator/stub-templates.test.ts +0 -7
  39. package/src/generator/stub-templates.ts +0 -14
  40. package/src/modules/finance-ledger/README.md +50 -0
  41. package/src/modules/finance-ledger/command/.gitkeep +0 -0
  42. package/src/modules/finance-ledger/command/addJournalLine.generated.ts +6 -0
  43. package/src/modules/finance-ledger/command/addJournalLine.test.ts +438 -0
  44. package/src/modules/finance-ledger/command/addJournalLine.ts +122 -0
  45. package/src/modules/finance-ledger/command/approveAndLockPeriod.generated.ts +6 -0
  46. package/src/modules/finance-ledger/command/approveAndLockPeriod.test.ts +107 -0
  47. package/src/modules/finance-ledger/command/approveAndLockPeriod.ts +72 -0
  48. package/src/modules/finance-ledger/command/beginClose.generated.ts +6 -0
  49. package/src/modules/finance-ledger/command/beginClose.test.ts +106 -0
  50. package/src/modules/finance-ledger/command/beginClose.ts +58 -0
  51. package/src/modules/finance-ledger/command/closePeriod.generated.ts +6 -0
  52. package/src/modules/finance-ledger/command/closePeriod.test.ts +87 -0
  53. package/src/modules/finance-ledger/command/closePeriod.ts +44 -0
  54. package/src/modules/finance-ledger/command/createAccountingPeriod.generated.ts +6 -0
  55. package/src/modules/finance-ledger/command/createAccountingPeriod.test.ts +425 -0
  56. package/src/modules/finance-ledger/command/createAccountingPeriod.ts +133 -0
  57. package/src/modules/finance-ledger/command/createFiscalYear.generated.ts +6 -0
  58. package/src/modules/finance-ledger/command/createFiscalYear.test.ts +197 -0
  59. package/src/modules/finance-ledger/command/createFiscalYear.ts +70 -0
  60. package/src/modules/finance-ledger/command/createJournalEntry.generated.ts +6 -0
  61. package/src/modules/finance-ledger/command/createJournalEntry.test.ts +261 -0
  62. package/src/modules/finance-ledger/command/createJournalEntry.ts +121 -0
  63. package/src/modules/finance-ledger/command/deleteAccountingPeriod.generated.ts +6 -0
  64. package/src/modules/finance-ledger/command/deleteAccountingPeriod.test.ts +71 -0
  65. package/src/modules/finance-ledger/command/deleteAccountingPeriod.ts +55 -0
  66. package/src/modules/finance-ledger/command/deleteFiscalYear.generated.ts +6 -0
  67. package/src/modules/finance-ledger/command/deleteFiscalYear.test.ts +38 -0
  68. package/src/modules/finance-ledger/command/deleteFiscalYear.ts +34 -0
  69. package/src/modules/finance-ledger/command/deleteJournalEntry.generated.ts +6 -0
  70. package/src/modules/finance-ledger/command/deleteJournalEntry.test.ts +58 -0
  71. package/src/modules/finance-ledger/command/deleteJournalEntry.ts +43 -0
  72. package/src/modules/finance-ledger/command/executeYearEndClose.generated.ts +6 -0
  73. package/src/modules/finance-ledger/command/executeYearEndClose.test.ts +239 -0
  74. package/src/modules/finance-ledger/command/executeYearEndClose.ts +415 -0
  75. package/src/modules/finance-ledger/command/finalCloseAndLockPeriod.generated.ts +6 -0
  76. package/src/modules/finance-ledger/command/finalCloseAndLockPeriod.test.ts +102 -0
  77. package/src/modules/finance-ledger/command/finalCloseAndLockPeriod.ts +76 -0
  78. package/src/modules/finance-ledger/command/finalizeFinancialStatement.generated.ts +6 -0
  79. package/src/modules/finance-ledger/command/finalizeFinancialStatement.test.ts +73 -0
  80. package/src/modules/finance-ledger/command/finalizeFinancialStatement.ts +73 -0
  81. package/src/modules/finance-ledger/command/generateFinancialStatement.generated.ts +6 -0
  82. package/src/modules/finance-ledger/command/generateFinancialStatement.test.ts +311 -0
  83. package/src/modules/finance-ledger/command/generateFinancialStatement.ts +275 -0
  84. package/src/modules/finance-ledger/command/generatePreliminaryStatements.generated.ts +6 -0
  85. package/src/modules/finance-ledger/command/generatePreliminaryStatements.test.ts +152 -0
  86. package/src/modules/finance-ledger/command/generatePreliminaryStatements.ts +140 -0
  87. package/src/modules/finance-ledger/command/generateTrialBalance.generated.ts +6 -0
  88. package/src/modules/finance-ledger/command/generateTrialBalance.test.ts +439 -0
  89. package/src/modules/finance-ledger/command/generateTrialBalance.ts +268 -0
  90. package/src/modules/finance-ledger/command/initiatePeriodClose.generated.ts +6 -0
  91. package/src/modules/finance-ledger/command/initiatePeriodClose.test.ts +153 -0
  92. package/src/modules/finance-ledger/command/initiatePeriodClose.ts +84 -0
  93. package/src/modules/finance-ledger/command/openForAdvanceEntry.generated.ts +6 -0
  94. package/src/modules/finance-ledger/command/openForAdvanceEntry.test.ts +87 -0
  95. package/src/modules/finance-ledger/command/openForAdvanceEntry.ts +44 -0
  96. package/src/modules/finance-ledger/command/openPeriod.generated.ts +6 -0
  97. package/src/modules/finance-ledger/command/openPeriod.test.ts +90 -0
  98. package/src/modules/finance-ledger/command/openPeriod.ts +44 -0
  99. package/src/modules/finance-ledger/command/permanentlyClosePeriod.generated.ts +6 -0
  100. package/src/modules/finance-ledger/command/permanentlyClosePeriod.test.ts +87 -0
  101. package/src/modules/finance-ledger/command/permanentlyClosePeriod.ts +48 -0
  102. package/src/modules/finance-ledger/command/postAdjustingEntries.generated.ts +6 -0
  103. package/src/modules/finance-ledger/command/postAdjustingEntries.test.ts +392 -0
  104. package/src/modules/finance-ledger/command/postAdjustingEntries.ts +156 -0
  105. package/src/modules/finance-ledger/command/postJournalEntry.generated.ts +6 -0
  106. package/src/modules/finance-ledger/command/postJournalEntry.test.ts +346 -0
  107. package/src/modules/finance-ledger/command/postJournalEntry.ts +160 -0
  108. package/src/modules/finance-ledger/command/processInventoryHandoff.generated.ts +6 -0
  109. package/src/modules/finance-ledger/command/processInventoryHandoff.test.ts +211 -0
  110. package/src/modules/finance-ledger/command/processInventoryHandoff.ts +133 -0
  111. package/src/modules/finance-ledger/command/processManufacturingHandoff.generated.ts +6 -0
  112. package/src/modules/finance-ledger/command/processManufacturingHandoff.test.ts +221 -0
  113. package/src/modules/finance-ledger/command/processManufacturingHandoff.ts +133 -0
  114. package/src/modules/finance-ledger/command/processPurchaseHandoff.generated.ts +6 -0
  115. package/src/modules/finance-ledger/command/processPurchaseHandoff.test.ts +222 -0
  116. package/src/modules/finance-ledger/command/processPurchaseHandoff.ts +133 -0
  117. package/src/modules/finance-ledger/command/processSalesHandoff.generated.ts +6 -0
  118. package/src/modules/finance-ledger/command/processSalesHandoff.test.ts +257 -0
  119. package/src/modules/finance-ledger/command/processSalesHandoff.ts +135 -0
  120. package/src/modules/finance-ledger/command/regenerateFinancialStatement.generated.ts +6 -0
  121. package/src/modules/finance-ledger/command/regenerateFinancialStatement.test.ts +129 -0
  122. package/src/modules/finance-ledger/command/regenerateFinancialStatement.ts +186 -0
  123. package/src/modules/finance-ledger/command/removeJournalLine.generated.ts +6 -0
  124. package/src/modules/finance-ledger/command/removeJournalLine.test.ts +65 -0
  125. package/src/modules/finance-ledger/command/removeJournalLine.ts +39 -0
  126. package/src/modules/finance-ledger/command/reopenPeriod.generated.ts +6 -0
  127. package/src/modules/finance-ledger/command/reopenPeriod.test.ts +87 -0
  128. package/src/modules/finance-ledger/command/reopenPeriod.ts +44 -0
  129. package/src/modules/finance-ledger/command/reverseJournalEntry.generated.ts +6 -0
  130. package/src/modules/finance-ledger/command/reverseJournalEntry.test.ts +337 -0
  131. package/src/modules/finance-ledger/command/reverseJournalEntry.ts +140 -0
  132. package/src/modules/finance-ledger/command/revertSoftLock.generated.ts +6 -0
  133. package/src/modules/finance-ledger/command/revertSoftLock.test.ts +96 -0
  134. package/src/modules/finance-ledger/command/revertSoftLock.ts +67 -0
  135. package/src/modules/finance-ledger/command/updateFiscalYear.generated.ts +6 -0
  136. package/src/modules/finance-ledger/command/updateFiscalYear.test.ts +138 -0
  137. package/src/modules/finance-ledger/command/updateFiscalYear.ts +85 -0
  138. package/src/modules/finance-ledger/command/updateJournalEntry.generated.ts +6 -0
  139. package/src/modules/finance-ledger/command/updateJournalEntry.test.ts +195 -0
  140. package/src/modules/finance-ledger/command/updateJournalEntry.ts +86 -0
  141. package/src/modules/finance-ledger/command/updateJournalLine.generated.ts +6 -0
  142. package/src/modules/finance-ledger/command/updateJournalLine.test.ts +385 -0
  143. package/src/modules/finance-ledger/command/updateJournalLine.ts +155 -0
  144. package/src/modules/finance-ledger/command/verifySubledgerTransfers.generated.ts +6 -0
  145. package/src/modules/finance-ledger/command/verifySubledgerTransfers.test.ts +201 -0
  146. package/src/modules/finance-ledger/command/verifySubledgerTransfers.ts +113 -0
  147. package/src/modules/finance-ledger/command/verifyTrialBalance.generated.ts +6 -0
  148. package/src/modules/finance-ledger/command/verifyTrialBalance.test.ts +136 -0
  149. package/src/modules/finance-ledger/command/verifyTrialBalance.ts +97 -0
  150. package/src/modules/finance-ledger/db/.gitkeep +0 -0
  151. package/src/modules/finance-ledger/db/accountingPeriod.ts +58 -0
  152. package/src/modules/finance-ledger/db/financialStatement.ts +92 -0
  153. package/src/modules/finance-ledger/db/financialStatementLineItem.ts +76 -0
  154. package/src/modules/finance-ledger/db/fiscalYear.ts +41 -0
  155. package/src/modules/finance-ledger/db/journalEntry.ts +101 -0
  156. package/src/modules/finance-ledger/db/journalLine.ts +64 -0
  157. package/src/modules/finance-ledger/db/periodClose.ts +97 -0
  158. package/src/modules/finance-ledger/db/trialBalance.ts +63 -0
  159. package/src/modules/finance-ledger/db/trialBalanceLine.ts +63 -0
  160. package/src/modules/finance-ledger/docs/commands/AddJournalLine.md +74 -0
  161. package/src/modules/finance-ledger/docs/commands/ApproveAndLockPeriod.md +53 -0
  162. package/src/modules/finance-ledger/docs/commands/BeginClose.md +47 -0
  163. package/src/modules/finance-ledger/docs/commands/ClosePeriod.md +45 -0
  164. package/src/modules/finance-ledger/docs/commands/CreateAccountingPeriod.md +69 -0
  165. package/src/modules/finance-ledger/docs/commands/CreateFiscalYear.md +56 -0
  166. package/src/modules/finance-ledger/docs/commands/CreateJournalEntry.md +63 -0
  167. package/src/modules/finance-ledger/docs/commands/DeleteAccountingPeriod.md +46 -0
  168. package/src/modules/finance-ledger/docs/commands/DeleteFiscalYear.md +40 -0
  169. package/src/modules/finance-ledger/docs/commands/DeleteJournalEntry.md +44 -0
  170. package/src/modules/finance-ledger/docs/commands/ExecuteYearEndClose.md +81 -0
  171. package/src/modules/finance-ledger/docs/commands/FinalCloseAndLockPeriod.md +49 -0
  172. package/src/modules/finance-ledger/docs/commands/FinalizeFinancialStatement.md +43 -0
  173. package/src/modules/finance-ledger/docs/commands/GenerateFinancialStatement.md +86 -0
  174. package/src/modules/finance-ledger/docs/commands/GeneratePreliminaryStatements.md +53 -0
  175. package/src/modules/finance-ledger/docs/commands/GenerateTrialBalance.md +75 -0
  176. package/src/modules/finance-ledger/docs/commands/InitiatePeriodClose.md +58 -0
  177. package/src/modules/finance-ledger/docs/commands/OpenForAdvanceEntry.md +44 -0
  178. package/src/modules/finance-ledger/docs/commands/OpenPeriod.md +45 -0
  179. package/src/modules/finance-ledger/docs/commands/PermanentlyClosePeriod.md +45 -0
  180. package/src/modules/finance-ledger/docs/commands/PostAdjustingEntries.md +61 -0
  181. package/src/modules/finance-ledger/docs/commands/PostJournalEntry.md +81 -0
  182. package/src/modules/finance-ledger/docs/commands/ProcessInventoryHandoff.md +72 -0
  183. package/src/modules/finance-ledger/docs/commands/ProcessManufacturingHandoff.md +68 -0
  184. package/src/modules/finance-ledger/docs/commands/ProcessPurchaseHandoff.md +68 -0
  185. package/src/modules/finance-ledger/docs/commands/ProcessSalesHandoff.md +71 -0
  186. package/src/modules/finance-ledger/docs/commands/RegenerateFinancialStatement.md +60 -0
  187. package/src/modules/finance-ledger/docs/commands/RemoveJournalLine.md +42 -0
  188. package/src/modules/finance-ledger/docs/commands/ReopenPeriod.md +45 -0
  189. package/src/modules/finance-ledger/docs/commands/ReverseJournalEntry.md +62 -0
  190. package/src/modules/finance-ledger/docs/commands/RevertSoftLock.md +49 -0
  191. package/src/modules/finance-ledger/docs/commands/UpdateFiscalYear.md +60 -0
  192. package/src/modules/finance-ledger/docs/commands/UpdateJournalEntry.md +50 -0
  193. package/src/modules/finance-ledger/docs/commands/UpdateJournalLine.md +61 -0
  194. package/src/modules/finance-ledger/docs/commands/VerifySubledgerTransfers.md +59 -0
  195. package/src/modules/finance-ledger/docs/commands/VerifyTrialBalance.md +53 -0
  196. package/src/modules/finance-ledger/docs/features/accounting-period-management.md +110 -0
  197. package/src/modules/finance-ledger/docs/features/financial-statement-generation.md +115 -0
  198. package/src/modules/finance-ledger/docs/features/journal-entry-management.md +138 -0
  199. package/src/modules/finance-ledger/docs/features/period-end-close.md +102 -0
  200. package/src/modules/finance-ledger/docs/features/subledger-integration.md +141 -0
  201. package/src/modules/finance-ledger/docs/features/trial-balance.md +99 -0
  202. package/src/modules/finance-ledger/docs/features/year-end-close.md +84 -0
  203. package/src/modules/finance-ledger/docs/models/AccountingPeriod.md +71 -0
  204. package/src/modules/finance-ledger/docs/models/FinancialStatement.md +76 -0
  205. package/src/modules/finance-ledger/docs/models/FinancialStatementLineItem.md +41 -0
  206. package/src/modules/finance-ledger/docs/models/FiscalYear.md +41 -0
  207. package/src/modules/finance-ledger/docs/models/JournalEntry.md +80 -0
  208. package/src/modules/finance-ledger/docs/models/JournalLine.md +47 -0
  209. package/src/modules/finance-ledger/docs/models/PeriodClose.md +83 -0
  210. package/src/modules/finance-ledger/docs/models/TrialBalance.md +56 -0
  211. package/src/modules/finance-ledger/docs/models/TrialBalanceLine.md +37 -0
  212. package/src/modules/finance-ledger/docs/queries/GetAccountingPeriod.md +35 -0
  213. package/src/modules/finance-ledger/docs/queries/GetFinancialStatement.md +38 -0
  214. package/src/modules/finance-ledger/docs/queries/GetFiscalYear.md +35 -0
  215. package/src/modules/finance-ledger/docs/queries/GetJournalEntry.md +37 -0
  216. package/src/modules/finance-ledger/docs/queries/GetPeriodByDate.md +38 -0
  217. package/src/modules/finance-ledger/docs/queries/GetPeriodClose.md +36 -0
  218. package/src/modules/finance-ledger/docs/queries/GetSubledgerTransferStatus.md +45 -0
  219. package/src/modules/finance-ledger/docs/queries/GetTrialBalance.md +38 -0
  220. package/src/modules/finance-ledger/docs/queries/ListAccountingPeriods.md +46 -0
  221. package/src/modules/finance-ledger/docs/queries/ListFinancialStatements.md +46 -0
  222. package/src/modules/finance-ledger/docs/queries/ListFiscalYears.md +42 -0
  223. package/src/modules/finance-ledger/docs/queries/ListJournalEntries.md +48 -0
  224. package/src/modules/finance-ledger/docs/queries/ListPeriodCloses.md +46 -0
  225. package/src/modules/finance-ledger/docs/queries/ListTrialBalances.md +51 -0
  226. package/src/modules/finance-ledger/executor/.gitkeep +0 -0
  227. package/src/modules/finance-ledger/generated/enums.ts +109 -0
  228. package/src/modules/finance-ledger/generated/kysely-tailordb.ts +202 -0
  229. package/src/modules/finance-ledger/index.ts +2 -0
  230. package/src/modules/finance-ledger/lib/_db_deps.ts +56 -0
  231. package/src/modules/finance-ledger/lib/errors.generated.ts +332 -0
  232. package/src/modules/finance-ledger/lib/permissions.generated.ts +41 -0
  233. package/src/modules/finance-ledger/lib/types.ts +66 -0
  234. package/src/modules/finance-ledger/module.ts +262 -0
  235. package/src/modules/finance-ledger/package.json +26 -0
  236. package/src/modules/finance-ledger/permissions.ts +3 -0
  237. package/src/modules/finance-ledger/query/.gitkeep +0 -0
  238. package/src/modules/finance-ledger/query/getAccountingPeriod.generated.ts +5 -0
  239. package/src/modules/finance-ledger/query/getAccountingPeriod.test.ts +31 -0
  240. package/src/modules/finance-ledger/query/getAccountingPeriod.ts +21 -0
  241. package/src/modules/finance-ledger/query/getFinancialStatement.generated.ts +5 -0
  242. package/src/modules/finance-ledger/query/getFinancialStatement.test.ts +35 -0
  243. package/src/modules/finance-ledger/query/getFinancialStatement.ts +29 -0
  244. package/src/modules/finance-ledger/query/getFiscalYear.generated.ts +5 -0
  245. package/src/modules/finance-ledger/query/getFiscalYear.test.ts +31 -0
  246. package/src/modules/finance-ledger/query/getFiscalYear.ts +21 -0
  247. package/src/modules/finance-ledger/query/getJournalEntry.generated.ts +5 -0
  248. package/src/modules/finance-ledger/query/getJournalEntry.test.ts +35 -0
  249. package/src/modules/finance-ledger/query/getJournalEntry.ts +29 -0
  250. package/src/modules/finance-ledger/query/getPeriodByDate.generated.ts +5 -0
  251. package/src/modules/finance-ledger/query/getPeriodByDate.test.ts +53 -0
  252. package/src/modules/finance-ledger/query/getPeriodByDate.ts +27 -0
  253. package/src/modules/finance-ledger/query/getPeriodClose.generated.ts +5 -0
  254. package/src/modules/finance-ledger/query/getPeriodClose.test.ts +31 -0
  255. package/src/modules/finance-ledger/query/getPeriodClose.ts +21 -0
  256. package/src/modules/finance-ledger/query/getSubledgerTransferStatus.generated.ts +5 -0
  257. package/src/modules/finance-ledger/query/getSubledgerTransferStatus.test.ts +101 -0
  258. package/src/modules/finance-ledger/query/getSubledgerTransferStatus.ts +68 -0
  259. package/src/modules/finance-ledger/query/getTrialBalance.generated.ts +5 -0
  260. package/src/modules/finance-ledger/query/getTrialBalance.test.ts +33 -0
  261. package/src/modules/finance-ledger/query/getTrialBalance.ts +30 -0
  262. package/src/modules/finance-ledger/query/listAccountingPeriods.generated.ts +5 -0
  263. package/src/modules/finance-ledger/query/listAccountingPeriods.test.ts +81 -0
  264. package/src/modules/finance-ledger/query/listAccountingPeriods.ts +61 -0
  265. package/src/modules/finance-ledger/query/listFinancialStatements.generated.ts +5 -0
  266. package/src/modules/finance-ledger/query/listFinancialStatements.test.ts +76 -0
  267. package/src/modules/finance-ledger/query/listFinancialStatements.ts +62 -0
  268. package/src/modules/finance-ledger/query/listFiscalYears.generated.ts +5 -0
  269. package/src/modules/finance-ledger/query/listFiscalYears.test.ts +63 -0
  270. package/src/modules/finance-ledger/query/listFiscalYears.ts +45 -0
  271. package/src/modules/finance-ledger/query/listJournalEntries.generated.ts +5 -0
  272. package/src/modules/finance-ledger/query/listJournalEntries.test.ts +91 -0
  273. package/src/modules/finance-ledger/query/listJournalEntries.ts +64 -0
  274. package/src/modules/finance-ledger/query/listPeriodCloses.generated.ts +5 -0
  275. package/src/modules/finance-ledger/query/listPeriodCloses.test.ts +63 -0
  276. package/src/modules/finance-ledger/query/listPeriodCloses.ts +64 -0
  277. package/src/modules/finance-ledger/query/listTrialBalances.generated.ts +5 -0
  278. package/src/modules/finance-ledger/query/listTrialBalances.test.ts +78 -0
  279. package/src/modules/finance-ledger/query/listTrialBalances.ts +56 -0
  280. package/src/modules/finance-ledger/seed/index.ts +19 -0
  281. package/src/modules/finance-ledger/tailor.config.ts +13 -0
  282. package/src/modules/finance-ledger/tailor.d.ts +13 -0
  283. package/src/modules/finance-ledger/testing/commandTestUtils.ts +35 -0
  284. package/src/modules/finance-ledger/testing/fixtures.ts +382 -0
  285. package/src/modules/finance-ledger/tsconfig.json +16 -0
  286. package/src/progress/schema.test.ts +161 -0
  287. package/src/progress/schema.ts +316 -0
  288. package/templates/scaffold/app/backend/package.json +1 -3
  289. package/templates/scaffold/app/backend/vitest.config.ts +4 -21
  290. package/src/generator/generate-stubs.ts +0 -35
@@ -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 "./initiatePeriodClose";
5
+
6
+ export const initiatePeriodClose = defineCommand(permissions.initiatePeriodClose, run);
@@ -0,0 +1,153 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ AccountingPeriodNotFoundError,
6
+ InvalidPeriodStatusError,
7
+ CloseAlreadyInProgressError,
8
+ ConcurrentCloseConflictError,
9
+ } from "../lib/errors.generated";
10
+ import {
11
+ baseAccountingPeriod,
12
+ neverOpenedPeriod,
13
+ closedPeriod,
14
+ permanentlyClosedPeriod,
15
+ basePeriodClose,
16
+ } from "../testing/fixtures";
17
+ import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
18
+ import { run } from "./initiatePeriodClose";
19
+
20
+ describe("initiatePeriodClose", () => {
21
+ it("returns error when accounting period does not exist", async () => {
22
+ const { db, spies } = createMockDb<Transaction>();
23
+ spies.select.mockReturnValueOnce(undefined); // period lookup
24
+
25
+ const result = await run(db, { accountingPeriodId: "nonexistent" }, commandCtx);
26
+
27
+ expectErr(result, AccountingPeriodNotFoundError);
28
+ });
29
+
30
+ it("returns error when accounting period is in NEVER_OPENED status", async () => {
31
+ const { db, spies } = createMockDb<Transaction>();
32
+ spies.select.mockReturnValueOnce(neverOpenedPeriod); // period lookup
33
+
34
+ const result = await run(db, { accountingPeriodId: neverOpenedPeriod.id }, commandCtx);
35
+
36
+ expectErr(result, InvalidPeriodStatusError);
37
+ });
38
+
39
+ it("returns error when accounting period is in CLOSED status", async () => {
40
+ const { db, spies } = createMockDb<Transaction>();
41
+ spies.select.mockReturnValueOnce(closedPeriod); // period lookup
42
+
43
+ const result = await run(db, { accountingPeriodId: closedPeriod.id }, commandCtx);
44
+
45
+ expectErr(result, InvalidPeriodStatusError);
46
+ });
47
+
48
+ it("returns error when accounting period is in PERMANENTLY_CLOSED status", async () => {
49
+ const { db, spies } = createMockDb<Transaction>();
50
+ spies.select.mockReturnValueOnce(permanentlyClosedPeriod); // period lookup
51
+
52
+ const result = await run(db, { accountingPeriodId: permanentlyClosedPeriod.id }, commandCtx);
53
+
54
+ expectErr(result, InvalidPeriodStatusError);
55
+ });
56
+
57
+ it("returns error when period already has an active close in progress", async () => {
58
+ const { db, spies } = createMockDb<Transaction>();
59
+ spies.select
60
+ .mockReturnValueOnce(baseAccountingPeriod) // period lookup
61
+ .mockReturnValueOnce(basePeriodClose); // existing active PeriodClose
62
+
63
+ const result = await run(db, { accountingPeriodId: baseAccountingPeriod.id }, commandCtx);
64
+
65
+ expectErr(result, CloseAlreadyInProgressError);
66
+ });
67
+
68
+ it("returns error on concurrent close attempt for the same period", async () => {
69
+ const { db, spies } = createMockDb<Transaction>();
70
+ spies.select
71
+ .mockReturnValueOnce(baseAccountingPeriod) // period lookup
72
+ .mockReturnValueOnce(undefined); // no existing PeriodClose
73
+ spies.insert.mockImplementationOnce(() => {
74
+ throw new Error("unique constraint violation");
75
+ });
76
+
77
+ const result = await run(db, { accountingPeriodId: baseAccountingPeriod.id }, commandCtx);
78
+
79
+ expectErr(result, ConcurrentCloseConflictError);
80
+ });
81
+
82
+ it("creates PeriodClose in NOT_STARTED status for an OPEN period", async () => {
83
+ const { db, spies } = createMockDb<Transaction>();
84
+ const createdPeriodClose = {
85
+ ...basePeriodClose,
86
+ initiatedByUserId: commandCtx.actorId,
87
+ };
88
+ spies.select
89
+ .mockReturnValueOnce(baseAccountingPeriod) // period lookup
90
+ .mockReturnValueOnce(undefined); // no existing PeriodClose
91
+ spies.insert.mockReturnValueOnce(createdPeriodClose);
92
+
93
+ const result = await run(db, { accountingPeriodId: baseAccountingPeriod.id }, commandCtx);
94
+
95
+ const value = expectOk(result);
96
+ expect(value.periodClose.status).toBe("NOT_STARTED");
97
+ });
98
+
99
+ it("records the initiating user's userId", async () => {
100
+ const { db, spies } = createMockDb<Transaction>();
101
+ const createdPeriodClose = {
102
+ ...basePeriodClose,
103
+ initiatedByUserId: commandCtx.actorId,
104
+ };
105
+ spies.select
106
+ .mockReturnValueOnce(baseAccountingPeriod) // period lookup
107
+ .mockReturnValueOnce(undefined); // no existing PeriodClose
108
+ spies.insert.mockReturnValueOnce(createdPeriodClose);
109
+
110
+ const result = await run(db, { accountingPeriodId: baseAccountingPeriod.id }, commandCtx);
111
+
112
+ const value = expectOk(result);
113
+ expect(value.periodClose.initiatedByUserId).toBe(commandCtx.actorId);
114
+ });
115
+
116
+ it("sets completionPercentage to 0", async () => {
117
+ const { db, spies } = createMockDb<Transaction>();
118
+ const createdPeriodClose = {
119
+ ...basePeriodClose,
120
+ initiatedByUserId: commandCtx.actorId,
121
+ completionPercentage: 0,
122
+ };
123
+ spies.select
124
+ .mockReturnValueOnce(baseAccountingPeriod) // period lookup
125
+ .mockReturnValueOnce(undefined); // no existing PeriodClose
126
+ spies.insert.mockReturnValueOnce(createdPeriodClose);
127
+
128
+ const result = await run(db, { accountingPeriodId: baseAccountingPeriod.id }, commandCtx);
129
+
130
+ const value = expectOk(result);
131
+ expect(value.periodClose.completionPercentage).toBe(0);
132
+ });
133
+
134
+ it("emits audit event recording acting user, timestamp, and period reference", async () => {
135
+ const { db, spies } = createMockDb<Transaction>();
136
+ const createdPeriodClose = {
137
+ ...basePeriodClose,
138
+ initiatedByUserId: commandCtx.actorId,
139
+ };
140
+ spies.select
141
+ .mockReturnValueOnce(baseAccountingPeriod) // period lookup
142
+ .mockReturnValueOnce(undefined); // no existing PeriodClose
143
+ spies.insert.mockReturnValueOnce(createdPeriodClose);
144
+
145
+ const result = await run(db, { accountingPeriodId: baseAccountingPeriod.id }, commandCtx);
146
+
147
+ const value = expectOk(result);
148
+ expect(value.auditEvent).toBeDefined();
149
+ expect(value.auditEvent.actorId).toBe(commandCtx.actorId);
150
+ expect(value.auditEvent.timestamp).toBeInstanceOf(Date);
151
+ expect(value.auditEvent.accountingPeriodId).toBe(baseAccountingPeriod.id);
152
+ });
153
+ });
@@ -0,0 +1,84 @@
1
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
2
+ import type { Transaction } from "../generated/kysely-tailordb";
3
+ import {
4
+ AccountingPeriodNotFoundError,
5
+ InvalidPeriodStatusError,
6
+ CloseAlreadyInProgressError,
7
+ ConcurrentCloseConflictError,
8
+ } from "../lib/errors.generated";
9
+
10
+ export interface InitiatePeriodCloseInput {
11
+ accountingPeriodId: string;
12
+ }
13
+
14
+ const TERMINAL_PERIOD_CLOSE_STATUSES = ["PERMANENTLY_CLOSED"];
15
+
16
+ /**
17
+ * Function: initiatePeriodClose
18
+ *
19
+ * Creates a new PeriodClose record in NOT_STARTED status for a given
20
+ * accounting period. Only periods in OPEN status can have a close initiated,
21
+ * and each period can have at most one active PeriodClose record.
22
+ */
23
+ export async function run(db: Transaction, input: InitiatePeriodCloseInput, ctx: CommandContext) {
24
+ const { accountingPeriodId } = input;
25
+
26
+ // 1. Find accounting period
27
+ const period = await db
28
+ .selectFrom("AccountingPeriod")
29
+ .selectAll()
30
+ .where("id", "=", accountingPeriodId)
31
+ .forUpdate()
32
+ .executeTakeFirst();
33
+
34
+ if (!period) {
35
+ return err(new AccountingPeriodNotFoundError(accountingPeriodId));
36
+ }
37
+
38
+ // 2. Validate period is in OPEN status
39
+ if (period.status !== "OPEN") {
40
+ return err(new InvalidPeriodStatusError(accountingPeriodId));
41
+ }
42
+
43
+ // 3. Check for existing active PeriodClose record
44
+ const existingClose = await db
45
+ .selectFrom("PeriodClose")
46
+ .selectAll()
47
+ .where("accountingPeriodId", "=", accountingPeriodId)
48
+ .forUpdate()
49
+ .executeTakeFirst();
50
+
51
+ if (existingClose && !TERMINAL_PERIOD_CLOSE_STATUSES.includes(existingClose.status)) {
52
+ return err(new CloseAlreadyInProgressError(accountingPeriodId));
53
+ }
54
+
55
+ // 4. Create PeriodClose record in NOT_STARTED status
56
+ const now = new Date();
57
+ let periodClose;
58
+ try {
59
+ periodClose = await db
60
+ .insertInto("PeriodClose")
61
+ .values({
62
+ accountingPeriodId,
63
+ status: "NOT_STARTED",
64
+ initiatedByUserId: ctx.actorId,
65
+ completionPercentage: 0,
66
+ yearEndCloseExecuted: false,
67
+ createdAt: now,
68
+ })
69
+ .returningAll()
70
+ .executeTakeFirstOrThrow();
71
+ } catch {
72
+ return err(new ConcurrentCloseConflictError(accountingPeriodId));
73
+ }
74
+
75
+ // 5. Build audit event
76
+ const auditEvent = {
77
+ type: "PERIOD_CLOSE_INITIATED",
78
+ actorId: ctx.actorId,
79
+ timestamp: now,
80
+ accountingPeriodId,
81
+ };
82
+
83
+ return ok({ periodClose, auditEvent });
84
+ }
@@ -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 "./openForAdvanceEntry";
5
+
6
+ export const openForAdvanceEntry = defineCommand(permissions.openForAdvanceEntry, run);
@@ -0,0 +1,87 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ AccountingPeriodNotFoundError,
6
+ InvalidStatusTransitionError,
7
+ } from "../lib/errors.generated";
8
+ import {
9
+ neverOpenedPeriod,
10
+ futureEnterablePeriod,
11
+ baseAccountingPeriod,
12
+ closedPeriod,
13
+ permanentlyClosedPeriod,
14
+ } from "../testing/fixtures";
15
+ import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
16
+ import { run } from "./openForAdvanceEntry";
17
+
18
+ describe("openForAdvanceEntry", () => {
19
+ it("returns error when accounting period does not exist", async () => {
20
+ const { db, spies } = createMockDb<Transaction>();
21
+ spies.select.mockReturnValueOnce(undefined);
22
+
23
+ const result = await run(db, { id: "non-existent" }, commandCtx);
24
+
25
+ expectErr(result, AccountingPeriodNotFoundError);
26
+ });
27
+
28
+ it("returns error when period is in FUTURE_ENTERABLE status", async () => {
29
+ const { db, spies } = createMockDb<Transaction>();
30
+ spies.select.mockReturnValueOnce(futureEnterablePeriod);
31
+
32
+ const result = await run(db, { id: futureEnterablePeriod.id }, commandCtx);
33
+
34
+ expectErr(result, InvalidStatusTransitionError);
35
+ });
36
+
37
+ it("returns error when period is in OPEN status", async () => {
38
+ const { db, spies } = createMockDb<Transaction>();
39
+ spies.select.mockReturnValueOnce(baseAccountingPeriod);
40
+
41
+ const result = await run(db, { id: baseAccountingPeriod.id }, commandCtx);
42
+
43
+ expectErr(result, InvalidStatusTransitionError);
44
+ });
45
+
46
+ it("returns error when period is in CLOSED status", async () => {
47
+ const { db, spies } = createMockDb<Transaction>();
48
+ spies.select.mockReturnValueOnce(closedPeriod);
49
+
50
+ const result = await run(db, { id: closedPeriod.id }, commandCtx);
51
+
52
+ expectErr(result, InvalidStatusTransitionError);
53
+ });
54
+
55
+ it("returns error when period is in PERMANENTLY_CLOSED status", async () => {
56
+ const { db, spies } = createMockDb<Transaction>();
57
+ spies.select.mockReturnValueOnce(permanentlyClosedPeriod);
58
+
59
+ const result = await run(db, { id: permanentlyClosedPeriod.id }, commandCtx);
60
+
61
+ expectErr(result, InvalidStatusTransitionError);
62
+ });
63
+
64
+ it("transitions NEVER_OPENED period to FUTURE_ENTERABLE", async () => {
65
+ const updatedPeriod = { ...neverOpenedPeriod, status: "FUTURE_ENTERABLE" };
66
+ const { db, spies } = createMockDb<Transaction>();
67
+ spies.select.mockReturnValueOnce(neverOpenedPeriod);
68
+ spies.update.mockReturnValueOnce(updatedPeriod);
69
+
70
+ const result = await run(db, { id: neverOpenedPeriod.id }, commandCtx);
71
+
72
+ const value = expectOk(result);
73
+ expect(value.accountingPeriod.status).toBe("FUTURE_ENTERABLE");
74
+ });
75
+
76
+ it("emits audit event recording status transition from NEVER_OPENED to FUTURE_ENTERABLE and acting user", async () => {
77
+ const updatedPeriod = { ...neverOpenedPeriod, status: "FUTURE_ENTERABLE" };
78
+ const { db, spies } = createMockDb<Transaction>();
79
+ spies.select.mockReturnValueOnce(neverOpenedPeriod);
80
+ spies.update.mockReturnValueOnce(updatedPeriod);
81
+
82
+ const result = await run(db, { id: neverOpenedPeriod.id }, commandCtx);
83
+
84
+ expectOk(result);
85
+ expect(spies.update).toHaveBeenCalled();
86
+ });
87
+ });
@@ -0,0 +1,44 @@
1
+ import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";
2
+ import type { Transaction } from "../generated/kysely-tailordb";
3
+ import {
4
+ AccountingPeriodNotFoundError,
5
+ InvalidStatusTransitionError,
6
+ } from "../lib/errors.generated";
7
+
8
+ export interface OpenForAdvanceEntryInput {
9
+ id: string;
10
+ }
11
+
12
+ const from = ["NEVER_OPENED"];
13
+
14
+ export async function run(db: Transaction, input: OpenForAdvanceEntryInput, ctx: CommandContext) {
15
+ const period = await db
16
+ .selectFrom("AccountingPeriod")
17
+ .selectAll()
18
+ .where("id", "=", input.id)
19
+ .forUpdate()
20
+ .executeTakeFirst();
21
+
22
+ if (!period) {
23
+ return err(new AccountingPeriodNotFoundError(input.id));
24
+ }
25
+
26
+ if (!from.includes(period.status as string)) {
27
+ return err(new InvalidStatusTransitionError(input.id));
28
+ }
29
+
30
+ const updated = await db
31
+ .updateTable("AccountingPeriod")
32
+ .set({
33
+ status: "FUTURE_ENTERABLE",
34
+ updatedAt: new Date(),
35
+ })
36
+ .where("id", "=", input.id)
37
+ .returningAll()
38
+ .executeTakeFirstOrThrow();
39
+
40
+ // Emit audit event recording status transition from NEVER_OPENED to FUTURE_ENTERABLE and acting user
41
+ void ctx;
42
+
43
+ return ok({ accountingPeriod: updated });
44
+ }
@@ -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 "./openPeriod";
5
+
6
+ export const openPeriod = defineCommand(permissions.openPeriod, run);
@@ -0,0 +1,90 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ AccountingPeriodNotFoundError,
6
+ InvalidStatusTransitionError,
7
+ } from "../lib/errors.generated";
8
+ import {
9
+ neverOpenedPeriod,
10
+ futureEnterablePeriod,
11
+ baseAccountingPeriod,
12
+ closedPeriod,
13
+ permanentlyClosedPeriod,
14
+ } from "../testing/fixtures";
15
+ import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
16
+ import { run } from "./openPeriod";
17
+
18
+ describe("openPeriod", () => {
19
+ it("returns error when accounting period does not exist", async () => {
20
+ const { db, spies } = createMockDb<Transaction>();
21
+ spies.select.mockReturnValueOnce(undefined);
22
+
23
+ const result = await run(db, { id: "non-existent" }, commandCtx);
24
+
25
+ expectErr(result, AccountingPeriodNotFoundError);
26
+ });
27
+
28
+ it("returns error when period is in OPEN status", async () => {
29
+ const { db, spies } = createMockDb<Transaction>();
30
+ spies.select.mockReturnValueOnce(baseAccountingPeriod);
31
+
32
+ const result = await run(db, { id: baseAccountingPeriod.id }, commandCtx);
33
+
34
+ expectErr(result, InvalidStatusTransitionError);
35
+ });
36
+
37
+ it("returns error when period is in CLOSED status", async () => {
38
+ const { db, spies } = createMockDb<Transaction>();
39
+ spies.select.mockReturnValueOnce(closedPeriod);
40
+
41
+ const result = await run(db, { id: closedPeriod.id }, commandCtx);
42
+
43
+ expectErr(result, InvalidStatusTransitionError);
44
+ });
45
+
46
+ it("returns error when period is in PERMANENTLY_CLOSED status", async () => {
47
+ const { db, spies } = createMockDb<Transaction>();
48
+ spies.select.mockReturnValueOnce(permanentlyClosedPeriod);
49
+
50
+ const result = await run(db, { id: permanentlyClosedPeriod.id }, commandCtx);
51
+
52
+ expectErr(result, InvalidStatusTransitionError);
53
+ });
54
+
55
+ it("transitions NEVER_OPENED period directly to OPEN", async () => {
56
+ const updatedPeriod = { ...neverOpenedPeriod, status: "OPEN" };
57
+ const { db, spies } = createMockDb<Transaction>();
58
+ spies.select.mockReturnValueOnce(neverOpenedPeriod);
59
+ spies.update.mockReturnValueOnce(updatedPeriod);
60
+
61
+ const result = await run(db, { id: neverOpenedPeriod.id }, commandCtx);
62
+
63
+ const value = expectOk(result);
64
+ expect(value.accountingPeriod.status).toBe("OPEN");
65
+ });
66
+
67
+ it("transitions FUTURE_ENTERABLE period to OPEN", async () => {
68
+ const updatedPeriod = { ...futureEnterablePeriod, status: "OPEN" };
69
+ const { db, spies } = createMockDb<Transaction>();
70
+ spies.select.mockReturnValueOnce(futureEnterablePeriod);
71
+ spies.update.mockReturnValueOnce(updatedPeriod);
72
+
73
+ const result = await run(db, { id: futureEnterablePeriod.id }, commandCtx);
74
+
75
+ const value = expectOk(result);
76
+ expect(value.accountingPeriod.status).toBe("OPEN");
77
+ });
78
+
79
+ it("emits audit event recording status transition and acting user", async () => {
80
+ const updatedPeriod = { ...neverOpenedPeriod, status: "OPEN" };
81
+ const { db, spies } = createMockDb<Transaction>();
82
+ spies.select.mockReturnValueOnce(neverOpenedPeriod);
83
+ spies.update.mockReturnValueOnce(updatedPeriod);
84
+
85
+ const result = await run(db, { id: neverOpenedPeriod.id }, commandCtx);
86
+
87
+ expectOk(result);
88
+ expect(spies.update).toHaveBeenCalled();
89
+ });
90
+ });
@@ -0,0 +1,44 @@
1
+ import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";
2
+ import type { Transaction } from "../generated/kysely-tailordb";
3
+ import {
4
+ AccountingPeriodNotFoundError,
5
+ InvalidStatusTransitionError,
6
+ } from "../lib/errors.generated";
7
+
8
+ export interface OpenPeriodInput {
9
+ id: string;
10
+ }
11
+
12
+ const from = ["NEVER_OPENED", "FUTURE_ENTERABLE"];
13
+
14
+ export async function run(db: Transaction, input: OpenPeriodInput, ctx: CommandContext) {
15
+ const period = await db
16
+ .selectFrom("AccountingPeriod")
17
+ .selectAll()
18
+ .where("id", "=", input.id)
19
+ .forUpdate()
20
+ .executeTakeFirst();
21
+
22
+ if (!period) {
23
+ return err(new AccountingPeriodNotFoundError(input.id));
24
+ }
25
+
26
+ if (!from.includes(period.status as string)) {
27
+ return err(new InvalidStatusTransitionError(input.id));
28
+ }
29
+
30
+ const updated = await db
31
+ .updateTable("AccountingPeriod")
32
+ .set({
33
+ status: "OPEN",
34
+ updatedAt: new Date(),
35
+ })
36
+ .where("id", "=", input.id)
37
+ .returningAll()
38
+ .executeTakeFirstOrThrow();
39
+
40
+ // Emit audit event recording status transition and acting user
41
+ void ctx;
42
+
43
+ return ok({ accountingPeriod: updated });
44
+ }
@@ -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 "./permanentlyClosePeriod";
5
+
6
+ export const permanentlyClosePeriod = defineCommand(permissions.permanentlyClosePeriod, run);
@@ -0,0 +1,87 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ AccountingPeriodNotFoundError,
6
+ InvalidStatusTransitionError,
7
+ } from "../lib/errors.generated";
8
+ import {
9
+ neverOpenedPeriod,
10
+ futureEnterablePeriod,
11
+ baseAccountingPeriod,
12
+ closedPeriod,
13
+ permanentlyClosedPeriod,
14
+ } from "../testing/fixtures";
15
+ import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
16
+ import { run } from "./permanentlyClosePeriod";
17
+
18
+ describe("permanentlyClosePeriod", () => {
19
+ it("returns error when accounting period does not exist", async () => {
20
+ const { db, spies } = createMockDb<Transaction>();
21
+ spies.select.mockReturnValueOnce(undefined);
22
+
23
+ const result = await run(db, { id: "non-existent" }, commandCtx);
24
+
25
+ expectErr(result, AccountingPeriodNotFoundError);
26
+ });
27
+
28
+ it("returns error when period is in NEVER_OPENED status", async () => {
29
+ const { db, spies } = createMockDb<Transaction>();
30
+ spies.select.mockReturnValueOnce(neverOpenedPeriod);
31
+
32
+ const result = await run(db, { id: neverOpenedPeriod.id }, commandCtx);
33
+
34
+ expectErr(result, InvalidStatusTransitionError);
35
+ });
36
+
37
+ it("returns error when period is in OPEN status", async () => {
38
+ const { db, spies } = createMockDb<Transaction>();
39
+ spies.select.mockReturnValueOnce(baseAccountingPeriod);
40
+
41
+ const result = await run(db, { id: baseAccountingPeriod.id }, commandCtx);
42
+
43
+ expectErr(result, InvalidStatusTransitionError);
44
+ });
45
+
46
+ it("returns error when period is in FUTURE_ENTERABLE status", async () => {
47
+ const { db, spies } = createMockDb<Transaction>();
48
+ spies.select.mockReturnValueOnce(futureEnterablePeriod);
49
+
50
+ const result = await run(db, { id: futureEnterablePeriod.id }, commandCtx);
51
+
52
+ expectErr(result, InvalidStatusTransitionError);
53
+ });
54
+
55
+ it("returns error when period is already PERMANENTLY_CLOSED", async () => {
56
+ const { db, spies } = createMockDb<Transaction>();
57
+ spies.select.mockReturnValueOnce(permanentlyClosedPeriod);
58
+
59
+ const result = await run(db, { id: permanentlyClosedPeriod.id }, commandCtx);
60
+
61
+ expectErr(result, InvalidStatusTransitionError);
62
+ });
63
+
64
+ it("transitions CLOSED period to PERMANENTLY_CLOSED", async () => {
65
+ const updatedPeriod = { ...closedPeriod, status: "PERMANENTLY_CLOSED" };
66
+ const { db, spies } = createMockDb<Transaction>();
67
+ spies.select.mockReturnValueOnce(closedPeriod);
68
+ spies.update.mockReturnValueOnce(updatedPeriod);
69
+
70
+ const result = await run(db, { id: closedPeriod.id }, commandCtx);
71
+
72
+ const value = expectOk(result);
73
+ expect(value.accountingPeriod.status).toBe("PERMANENTLY_CLOSED");
74
+ });
75
+
76
+ it("emits audit event recording the irreversible transition and acting user", async () => {
77
+ const updatedPeriod = { ...closedPeriod, status: "PERMANENTLY_CLOSED" };
78
+ const { db, spies } = createMockDb<Transaction>();
79
+ spies.select.mockReturnValueOnce(closedPeriod);
80
+ spies.update.mockReturnValueOnce(updatedPeriod);
81
+
82
+ const result = await run(db, { id: closedPeriod.id }, commandCtx);
83
+
84
+ expectOk(result);
85
+ expect(spies.update).toHaveBeenCalled();
86
+ });
87
+ });
@@ -0,0 +1,48 @@
1
+ import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";
2
+ import type { Transaction } from "../generated/kysely-tailordb";
3
+ import {
4
+ AccountingPeriodNotFoundError,
5
+ InvalidStatusTransitionError,
6
+ } from "../lib/errors.generated";
7
+
8
+ export interface PermanentlyClosePeriodInput {
9
+ id: string;
10
+ }
11
+
12
+ const from = ["CLOSED"];
13
+
14
+ export async function run(
15
+ db: Transaction,
16
+ input: PermanentlyClosePeriodInput,
17
+ ctx: CommandContext,
18
+ ) {
19
+ const period = await db
20
+ .selectFrom("AccountingPeriod")
21
+ .selectAll()
22
+ .where("id", "=", input.id)
23
+ .forUpdate()
24
+ .executeTakeFirst();
25
+
26
+ if (!period) {
27
+ return err(new AccountingPeriodNotFoundError(input.id));
28
+ }
29
+
30
+ if (!from.includes(period.status as string)) {
31
+ return err(new InvalidStatusTransitionError(input.id));
32
+ }
33
+
34
+ const updated = await db
35
+ .updateTable("AccountingPeriod")
36
+ .set({
37
+ status: "PERMANENTLY_CLOSED",
38
+ updatedAt: new Date(),
39
+ })
40
+ .where("id", "=", input.id)
41
+ .returningAll()
42
+ .executeTakeFirstOrThrow();
43
+
44
+ // Emit audit event recording the irreversible transition and acting user
45
+ void ctx;
46
+
47
+ return ok({ accountingPeriod: updated });
48
+ }
@@ -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 "./postAdjustingEntries";
5
+
6
+ export const postAdjustingEntries = defineCommand(permissions.postAdjustingEntries, run);