@tailor-platform/erp-kit 0.0.1 → 0.1.1

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 (231) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +196 -28
  4. package/dist/cli.js +914 -0
  5. package/package.json +67 -8
  6. package/schemas/app-compose/actors.yml +34 -0
  7. package/schemas/app-compose/business-flow.yml +50 -0
  8. package/schemas/app-compose/requirements.yml +33 -0
  9. package/schemas/app-compose/resolver.yml +47 -0
  10. package/schemas/app-compose/screen.yml +81 -0
  11. package/schemas/app-compose/story.yml +67 -0
  12. package/schemas/module/command.yml +52 -0
  13. package/schemas/module/feature.yml +58 -0
  14. package/schemas/module/model.yml +70 -0
  15. package/schemas/module/module.yml +50 -0
  16. package/skills/1-module-docs/SKILL.md +111 -0
  17. package/skills/1-module-docs/references/structure.md +22 -0
  18. package/skills/2-module-feature-breakdown/SKILL.md +72 -0
  19. package/skills/2-module-feature-breakdown/references/commands.md +48 -0
  20. package/skills/2-module-feature-breakdown/references/models.md +29 -0
  21. package/skills/2-module-feature-breakdown/references/structure.md +22 -0
  22. package/skills/3-module-doc-review/SKILL.md +236 -0
  23. package/skills/3-module-doc-review/references/commands.md +54 -0
  24. package/skills/3-module-doc-review/references/models.md +29 -0
  25. package/skills/3-module-doc-review/references/testing.md +37 -0
  26. package/skills/4-module-tdd-implementation/SKILL.md +74 -0
  27. package/skills/4-module-tdd-implementation/references/commands.md +45 -0
  28. package/skills/4-module-tdd-implementation/references/db-relations.md +69 -0
  29. package/skills/4-module-tdd-implementation/references/errors.md +7 -0
  30. package/skills/4-module-tdd-implementation/references/exports.md +8 -0
  31. package/skills/4-module-tdd-implementation/references/models.md +30 -0
  32. package/skills/4-module-tdd-implementation/references/structure.md +22 -0
  33. package/skills/4-module-tdd-implementation/references/testing.md +37 -0
  34. package/skills/5-module-implementation-review/SKILL.md +408 -0
  35. package/skills/5-module-implementation-review/references/commands.md +45 -0
  36. package/skills/5-module-implementation-review/references/errors.md +7 -0
  37. package/skills/5-module-implementation-review/references/exports.md +8 -0
  38. package/skills/5-module-implementation-review/references/models.md +30 -0
  39. package/skills/5-module-implementation-review/references/testing.md +29 -0
  40. package/skills/app-compose-1-requirement-analysis/SKILL.md +89 -0
  41. package/skills/app-compose-1-requirement-analysis/references/structure.md +27 -0
  42. package/skills/app-compose-2-requirements-breakdown/SKILL.md +95 -0
  43. package/skills/app-compose-2-requirements-breakdown/references/screen-detailview.md +106 -0
  44. package/skills/app-compose-2-requirements-breakdown/references/screen-form.md +139 -0
  45. package/skills/app-compose-2-requirements-breakdown/references/screen-listview.md +153 -0
  46. package/skills/app-compose-2-requirements-breakdown/references/structure.md +27 -0
  47. package/skills/app-compose-3-doc-review/SKILL.md +116 -0
  48. package/skills/app-compose-3-doc-review/references/structure.md +27 -0
  49. package/skills/app-compose-4-design-mock/SKILL.md +256 -0
  50. package/skills/app-compose-4-design-mock/references/component.md +50 -0
  51. package/skills/app-compose-4-design-mock/references/screen-detailview.md +106 -0
  52. package/skills/app-compose-4-design-mock/references/screen-form.md +139 -0
  53. package/skills/app-compose-4-design-mock/references/screen-listview.md +153 -0
  54. package/skills/app-compose-4-design-mock/references/structure.md +27 -0
  55. package/skills/app-compose-5-design-mock-review/SKILL.md +290 -0
  56. package/skills/app-compose-5-design-mock-review/references/component.md +50 -0
  57. package/skills/app-compose-5-design-mock-review/references/screen-detailview.md +106 -0
  58. package/skills/app-compose-5-design-mock-review/references/screen-form.md +139 -0
  59. package/skills/app-compose-5-design-mock-review/references/screen-listview.md +153 -0
  60. package/skills/app-compose-6-implementation-spec/SKILL.md +127 -0
  61. package/skills/app-compose-6-implementation-spec/references/auth.md +72 -0
  62. package/skills/app-compose-6-implementation-spec/references/structure.md +27 -0
  63. package/skills/mock-scenario/SKILL.md +118 -0
  64. package/src/app.ts +1 -0
  65. package/src/cli.ts +120 -0
  66. package/src/commands/check.test.ts +30 -0
  67. package/src/commands/check.ts +66 -0
  68. package/src/commands/init.test.ts +88 -0
  69. package/src/commands/init.ts +120 -0
  70. package/src/commands/mock/index.ts +53 -0
  71. package/src/commands/mock/start.ts +179 -0
  72. package/src/commands/mock/validate.test.ts +185 -0
  73. package/src/commands/mock/validate.ts +198 -0
  74. package/src/commands/scaffold.test.ts +76 -0
  75. package/src/commands/scaffold.ts +119 -0
  76. package/src/commands/sync-check.test.ts +125 -0
  77. package/src/commands/sync-check.ts +182 -0
  78. package/src/integration.test.ts +63 -0
  79. package/src/mdschema.ts +48 -0
  80. package/src/mockServer.ts +55 -0
  81. package/src/module.ts +86 -0
  82. package/src/modules/accounting/.gitkeep +0 -0
  83. package/src/modules/coa-management/.gitkeep +0 -0
  84. package/src/modules/inventory/.gitkeep +0 -0
  85. package/src/modules/manufacturing/.gitkeep +0 -0
  86. package/src/modules/primitives/README.md +39 -0
  87. package/src/modules/primitives/command/activateCategory.test.ts +75 -0
  88. package/src/modules/primitives/command/activateCategory.ts +50 -0
  89. package/src/modules/primitives/command/activateCurrency.test.ts +70 -0
  90. package/src/modules/primitives/command/activateCurrency.ts +50 -0
  91. package/src/modules/primitives/command/activateUnit.test.ts +53 -0
  92. package/src/modules/primitives/command/activateUnit.ts +50 -0
  93. package/src/modules/primitives/command/convertAmount.test.ts +275 -0
  94. package/src/modules/primitives/command/convertAmount.ts +126 -0
  95. package/src/modules/primitives/command/convertQuantity.test.ts +219 -0
  96. package/src/modules/primitives/command/convertQuantity.ts +73 -0
  97. package/src/modules/primitives/command/createCategory.test.ts +126 -0
  98. package/src/modules/primitives/command/createCategory.ts +89 -0
  99. package/src/modules/primitives/command/createCurrency.test.ts +191 -0
  100. package/src/modules/primitives/command/createCurrency.ts +77 -0
  101. package/src/modules/primitives/command/createExchangeRate.test.ts +216 -0
  102. package/src/modules/primitives/command/createExchangeRate.ts +91 -0
  103. package/src/modules/primitives/command/createUnit.test.ts +214 -0
  104. package/src/modules/primitives/command/createUnit.ts +88 -0
  105. package/src/modules/primitives/command/deactivateCategory.test.ts +97 -0
  106. package/src/modules/primitives/command/deactivateCategory.ts +62 -0
  107. package/src/modules/primitives/command/deactivateCurrency.test.ts +85 -0
  108. package/src/modules/primitives/command/deactivateCurrency.ts +55 -0
  109. package/src/modules/primitives/command/deactivateUnit.test.ts +78 -0
  110. package/src/modules/primitives/command/deactivateUnit.ts +62 -0
  111. package/src/modules/primitives/command/setBaseCurrency.test.ts +98 -0
  112. package/src/modules/primitives/command/setBaseCurrency.ts +74 -0
  113. package/src/modules/primitives/command/setReferenceUnit.test.ts +108 -0
  114. package/src/modules/primitives/command/setReferenceUnit.ts +84 -0
  115. package/src/modules/primitives/db/currency.ts +30 -0
  116. package/src/modules/primitives/db/exchangeRate.ts +28 -0
  117. package/src/modules/primitives/db/unit.ts +32 -0
  118. package/src/modules/primitives/db/uomCategory.ts +32 -0
  119. package/src/modules/primitives/docs/commands/ActivateCategory.md +34 -0
  120. package/src/modules/primitives/docs/commands/ActivateCurrency.md +33 -0
  121. package/src/modules/primitives/docs/commands/ActivateUnit.md +34 -0
  122. package/src/modules/primitives/docs/commands/ConvertAmount.md +50 -0
  123. package/src/modules/primitives/docs/commands/ConvertQuantity.md +43 -0
  124. package/src/modules/primitives/docs/commands/CreateCategory.md +44 -0
  125. package/src/modules/primitives/docs/commands/CreateCurrency.md +47 -0
  126. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +48 -0
  127. package/src/modules/primitives/docs/commands/CreateUnit.md +48 -0
  128. package/src/modules/primitives/docs/commands/DeactivateCategory.md +38 -0
  129. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +38 -0
  130. package/src/modules/primitives/docs/commands/DeactivateUnit.md +38 -0
  131. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +39 -0
  132. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +43 -0
  133. package/src/modules/primitives/docs/features/currency-definitions.md +55 -0
  134. package/src/modules/primitives/docs/features/exchange-rates.md +61 -0
  135. package/src/modules/primitives/docs/features/unit-conversion.md +66 -0
  136. package/src/modules/primitives/docs/features/uom-categories.md +52 -0
  137. package/src/modules/primitives/docs/models/Currency.md +45 -0
  138. package/src/modules/primitives/docs/models/ExchangeRate.md +33 -0
  139. package/src/modules/primitives/docs/models/Unit.md +46 -0
  140. package/src/modules/primitives/docs/models/UoMCategory.md +44 -0
  141. package/src/modules/primitives/generated/kysely-tailordb.ts +95 -0
  142. package/src/modules/primitives/index.ts +40 -0
  143. package/src/modules/primitives/lib/errors.ts +138 -0
  144. package/src/modules/primitives/lib/types.ts +20 -0
  145. package/src/modules/primitives/module.ts +66 -0
  146. package/src/modules/primitives/permissions.ts +18 -0
  147. package/src/modules/primitives/tailor.config.ts +11 -0
  148. package/src/modules/primitives/testing/fixtures.ts +161 -0
  149. package/src/modules/product-management/.gitkeep +0 -0
  150. package/src/modules/purchase/.gitkeep +0 -0
  151. package/src/modules/sales/.gitkeep +0 -0
  152. package/src/modules/shared/createContext.test.ts +39 -0
  153. package/src/modules/shared/createContext.ts +15 -0
  154. package/src/modules/shared/defineCommand.test.ts +42 -0
  155. package/src/modules/shared/defineCommand.ts +19 -0
  156. package/src/modules/shared/definePermissions.test.ts +146 -0
  157. package/src/modules/shared/definePermissions.ts +94 -0
  158. package/src/modules/shared/entityTypes.ts +15 -0
  159. package/src/modules/shared/errors.ts +22 -0
  160. package/src/modules/shared/index.ts +1 -0
  161. package/src/modules/shared/internal.ts +13 -0
  162. package/src/modules/shared/requirePermission.test.ts +47 -0
  163. package/src/modules/shared/requirePermission.ts +8 -0
  164. package/src/modules/shared/types.ts +4 -0
  165. package/src/modules/supplier-management/.gitkeep +0 -0
  166. package/src/modules/supplier-portal/.gitkeep +0 -0
  167. package/src/modules/testing/index.ts +120 -0
  168. package/src/modules/user-management/README.md +38 -0
  169. package/src/modules/user-management/command/activateUser.test.ts +112 -0
  170. package/src/modules/user-management/command/activateUser.ts +67 -0
  171. package/src/modules/user-management/command/assignPermissionToRole.test.ts +119 -0
  172. package/src/modules/user-management/command/assignPermissionToRole.ts +87 -0
  173. package/src/modules/user-management/command/assignRoleToUser.test.ts +162 -0
  174. package/src/modules/user-management/command/assignRoleToUser.ts +93 -0
  175. package/src/modules/user-management/command/createPermission.test.ts +143 -0
  176. package/src/modules/user-management/command/createPermission.ts +66 -0
  177. package/src/modules/user-management/command/createRole.test.ts +115 -0
  178. package/src/modules/user-management/command/createRole.ts +52 -0
  179. package/src/modules/user-management/command/createUser.test.ts +198 -0
  180. package/src/modules/user-management/command/createUser.ts +85 -0
  181. package/src/modules/user-management/command/deactivateUser.test.ts +112 -0
  182. package/src/modules/user-management/command/deactivateUser.ts +67 -0
  183. package/src/modules/user-management/command/logAuditEvent.test.ts +179 -0
  184. package/src/modules/user-management/command/logAuditEvent.ts +59 -0
  185. package/src/modules/user-management/command/reactivateUser.test.ts +115 -0
  186. package/src/modules/user-management/command/reactivateUser.ts +67 -0
  187. package/src/modules/user-management/command/revokePermissionFromRole.test.ts +112 -0
  188. package/src/modules/user-management/command/revokePermissionFromRole.ts +81 -0
  189. package/src/modules/user-management/command/revokeRoleFromUser.test.ts +112 -0
  190. package/src/modules/user-management/command/revokeRoleFromUser.ts +81 -0
  191. package/src/modules/user-management/db/auditEvent.ts +47 -0
  192. package/src/modules/user-management/db/permission.ts +31 -0
  193. package/src/modules/user-management/db/role.ts +28 -0
  194. package/src/modules/user-management/db/rolePermission.ts +44 -0
  195. package/src/modules/user-management/db/user.ts +38 -0
  196. package/src/modules/user-management/db/userRole.ts +44 -0
  197. package/src/modules/user-management/docs/commands/ActivateUser.md +36 -0
  198. package/src/modules/user-management/docs/commands/AssignPermissionToRole.md +39 -0
  199. package/src/modules/user-management/docs/commands/AssignRoleToUser.md +43 -0
  200. package/src/modules/user-management/docs/commands/CreatePermission.md +35 -0
  201. package/src/modules/user-management/docs/commands/CreateRole.md +35 -0
  202. package/src/modules/user-management/docs/commands/CreateUser.md +41 -0
  203. package/src/modules/user-management/docs/commands/DeactivateUser.md +38 -0
  204. package/src/modules/user-management/docs/commands/LogAuditEvent.md +37 -0
  205. package/src/modules/user-management/docs/commands/ReactivateUser.md +37 -0
  206. package/src/modules/user-management/docs/commands/RevokePermissionFromRole.md +40 -0
  207. package/src/modules/user-management/docs/commands/RevokeRoleFromUser.md +40 -0
  208. package/src/modules/user-management/docs/features/audit-trail.md +80 -0
  209. package/src/modules/user-management/docs/features/role-based-access-control.md +76 -0
  210. package/src/modules/user-management/docs/features/user-account-management.md +64 -0
  211. package/src/modules/user-management/docs/models/AuditEvent.md +34 -0
  212. package/src/modules/user-management/docs/models/Permission.md +31 -0
  213. package/src/modules/user-management/docs/models/Role.md +31 -0
  214. package/src/modules/user-management/docs/models/RolePermission.md +33 -0
  215. package/src/modules/user-management/docs/models/User.md +47 -0
  216. package/src/modules/user-management/docs/models/UserRole.md +34 -0
  217. package/src/modules/user-management/docs/plans/2026-01-30-flattened-permissions-design.md +52 -0
  218. package/src/modules/user-management/executor/recomputeOnRolePermissionChange.ts +61 -0
  219. package/src/modules/user-management/generated/enums.ts +24 -0
  220. package/src/modules/user-management/generated/kysely-tailordb.ts +112 -0
  221. package/src/modules/user-management/index.ts +32 -0
  222. package/src/modules/user-management/lib/errors.ts +81 -0
  223. package/src/modules/user-management/lib/recomputeUserPermissions.ts +53 -0
  224. package/src/modules/user-management/lib/types.ts +31 -0
  225. package/src/modules/user-management/module.ts +77 -0
  226. package/src/modules/user-management/permissions.ts +15 -0
  227. package/src/modules/user-management/tailor.config.ts +11 -0
  228. package/src/modules/user-management/testing/fixtures.ts +98 -0
  229. package/src/schemas.ts +25 -0
  230. package/src/testing.ts +10 -0
  231. package/src/util.ts +3 -0
@@ -0,0 +1,61 @@
1
+ # Exchange Rates
2
+
3
+ ## Overview
4
+
5
+ Exchange Rates maintain the conversion ratios between currency pairs with effective dates. Each rate record specifies the source currency, target currency, rate value, and the date from which it applies. The system uses these rates to convert monetary amounts for transactions, reporting, and consolidation.
6
+
7
+ Rates are date-based to support historical accuracy - a transaction from last month uses last month's rate, not today's rate.
8
+
9
+ ## Business Purpose
10
+
11
+ Multi-currency operations require accurate exchange rate management:
12
+
13
+ - **Transaction Recording**: Convert foreign currency invoices to base currency at booking time
14
+ - **Financial Reporting**: Consolidate subsidiaries using period-end rates
15
+ - **Historical Accuracy**: Audit trail requires rates as of transaction date
16
+ - **Variance Analysis**: Compare budgeted rates vs actual rates for currency exposure
17
+
18
+ Exchange rates ensure monetary consistency while maintaining complete audit traceability.
19
+
20
+ ## Process Flow
21
+
22
+ ```mermaid
23
+ flowchart TD
24
+ A[Create Exchange Rate] --> B[Set Currency Pair]
25
+ B --> C[Set Rate Value]
26
+ C --> D[Set Effective Date]
27
+ D --> E{Validate Rate}
28
+ E -->|Invalid| F[Return Error]
29
+ E -->|Valid| G[Save Rate]
30
+ G --> H[Rate Available for Date Range]
31
+
32
+ I[Convert Amount] --> J[Find Rate for Date]
33
+ J --> K{Rate Found?}
34
+ K -->|No| L[Return Error or Use Fallback]
35
+ K -->|Yes| M[Apply Rate × Amount]
36
+ M --> N[Return Converted Amount]
37
+ ```
38
+
39
+ ## Scenario Patterns
40
+
41
+ - **Daily Rate Updates**: Finance team updates rates daily from central bank or market data feed
42
+ - **Period-End Rates**: Month-end closing uses official rates for consolidation
43
+ - **Historical Lookup**: Generating report for Q1 uses Q1 rates, not current rates
44
+ - **Rate Gap Handling**: Transaction on holiday uses most recent prior rate when exact date unavailable
45
+ - **Inverse Calculation**: Rate for USD→EUR automatically provides EUR→USD by inversion
46
+
47
+ ## Test Cases
48
+
49
+ - Creating rate with valid currency pair and positive rate should succeed
50
+ - Rate with zero or negative value should fail validation
51
+ - Rate effective date in far future should be allowed (forward rates)
52
+ - Looking up rate for date should return most recent rate on or before that date
53
+ - Looking up rate with no prior rate should return error or configurable fallback
54
+ - Same currency conversion (USD→USD) should return rate of 1.0
55
+ - Inverse rate calculation should be mathematically correct (1/rate)
56
+ - Updating existing rate for same date should replace previous value
57
+
58
+ ## Reference Links
59
+
60
+ - [European Central Bank Exchange Rates](https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html)
61
+ - [Open Exchange Rates API](https://openexchangerates.org/)
@@ -0,0 +1,66 @@
1
+ # Unit Conversion
2
+
3
+ ## Overview
4
+
5
+ Unit Conversion enables automatic quantity transformation between any two units within the same category. The conversion uses the reference unit as an intermediary, ensuring consistent results regardless of which units are involved. This feature supports the core ERP operations where quantities must be expressed in different units for purchasing, inventory, and sales.
6
+
7
+ Conversions include configurable rounding precision to match business requirements for different unit types.
8
+
9
+ ## Business Purpose
10
+
11
+ Different stakeholders work with different units:
12
+
13
+ - **Purchasing** buys in bulk units (cases, pallets, drums)
14
+ - **Warehouse** tracks in standardized units (pieces, kilograms, liters)
15
+ - **Sales** sells in customer-friendly units (packs, bottles, individual items)
16
+ - **Manufacturing** measures in precise units (grams, milliliters)
17
+
18
+ Unit conversion eliminates manual calculation errors and ensures inventory accuracy when the same product moves through these different contexts.
19
+
20
+ ## Process Flow
21
+
22
+ ```mermaid
23
+ flowchart TD
24
+ A[Conversion Request] --> B{Same Category?}
25
+ B -->|No| C[Return Error: Incompatible Units]
26
+ B -->|Yes| D[Get Source Unit Factor]
27
+ D --> E[Get Target Unit Factor]
28
+ E --> F[Calculate: Qty × Source Factor ÷ Target Factor]
29
+ F --> G[Apply Rounding Precision]
30
+ G --> H[Return Converted Quantity]
31
+
32
+ subgraph Validation
33
+ B
34
+ end
35
+
36
+ subgraph Calculation
37
+ D
38
+ E
39
+ F
40
+ G
41
+ end
42
+ ```
43
+
44
+ ## Scenario Patterns
45
+
46
+ - **Purchase to Inventory**: Vendor ships 10 cases (12 units/case), system records 120 pieces in inventory
47
+ - **Sales Order Fulfillment**: Customer orders 5 kg, warehouse picks 5000 grams from bin tracked in grams
48
+ - **Manufacturing Consumption**: Recipe calls for 500ml, inventory deducts 0.5 liters from stock
49
+ - **Cross-Unit Reporting**: Generate report showing total weight in both kilograms and pounds for different markets
50
+ - **Fractional Handling**: Order for 2.5 dozen converts to 30 pieces for picking
51
+
52
+ ## Test Cases
53
+
54
+ - Converting between units in the same category should return correct result
55
+ - Converting between units in different categories should return an error
56
+ - Converting with the same source and target unit should return the original quantity
57
+ - Converting to/from the reference unit should use factor of 1.0
58
+ - Rounding should respect the target unit's precision setting
59
+ - Zero quantity conversion should return zero
60
+ - Negative quantity conversion should be handled consistently (error or allow based on business rules)
61
+ - Very large quantities should not cause overflow errors
62
+
63
+ ## Reference Links
64
+
65
+ - [NIST Unit Conversion Guide](https://www.nist.gov/pml/owm/metric-si/unit-conversion)
66
+ - [Odoo UoM Conversion Documentation](https://www.odoo.com/documentation/19.0/applications/inventory_and_mrp/inventory/product_management/configure/uom.html)
@@ -0,0 +1,52 @@
1
+ # UoM Categories
2
+
3
+ ## Overview
4
+
5
+ UoM Categories organize units of measure into logical groupings where conversions are meaningful. Each category has a designated reference unit that serves as the base for all conversion calculations within that category. Units can only be converted to other units within the same category.
6
+
7
+ This feature enables businesses to define their measurement standards while maintaining mathematical consistency across all unit conversions.
8
+
9
+ ## Business Purpose
10
+
11
+ Organizations need to track quantities in various units depending on context - purchasing in bulk units, selling in retail units, and managing inventory in standardized units. UoM Categories ensure that:
12
+
13
+ - Conversions only occur between compatible units (you cannot convert kilograms to liters)
14
+ - A single source of truth exists for conversion factors via the reference unit
15
+ - New units can be added without updating relationships to every other unit
16
+ - Mathematical precision is maintained through a single conversion path
17
+
18
+ ## Process Flow
19
+
20
+ ```mermaid
21
+ flowchart TD
22
+ A[Create Category] --> B{Has Reference Unit?}
23
+ B -->|No| C[Create Reference Unit]
24
+ C --> D[Set as Category Reference]
25
+ B -->|Yes| E[Add Additional Units]
26
+ D --> E
27
+ E --> F[Define Conversion Factor]
28
+ F --> G{More Units?}
29
+ G -->|Yes| E
30
+ G -->|No| H[Category Ready]
31
+ H --> I[Products Can Reference Units]
32
+ ```
33
+
34
+ ## Scenario Patterns
35
+
36
+ - **Initial Setup**: Administrator creates standard categories (Unit, Weight, Volume, Length, Time) with reference units during system initialization
37
+ - **Industry-Specific Units**: Manufacturing adds custom units like "Pallet" or "Roll" to the Unit category with appropriate conversion factors
38
+ - **Regional Adaptation**: Business operating in multiple regions adds both metric and imperial units to Weight and Length categories
39
+ - **Precision Requirements**: Pharmaceutical company creates units with high decimal precision for precise dosage tracking
40
+
41
+ ## Test Cases
42
+
43
+ - Creating a category without a reference unit should fail or prompt for reference unit creation
44
+ - Setting a reference unit should automatically set its conversion factor to 1.0
45
+ - Deleting a reference unit should be prevented if other units exist in the category
46
+ - Category names must be unique within the system
47
+ - Deactivating a category should prevent new products from using its units
48
+
49
+ ## Reference Links
50
+
51
+ - [Odoo Units of Measure Documentation](https://www.odoo.com/documentation/19.0/applications/inventory_and_mrp/inventory/product_management/configure/uom.html)
52
+ - [ISO 80000 Quantities and Units Standard](https://www.iso.org/standard/76921.html)
@@ -0,0 +1,45 @@
1
+ # Currency
2
+
3
+ ## Description
4
+
5
+ Currency defines a monetary unit available for use across the ERP system. Each currency contains the ISO 4217 code, display symbol, name, and decimal precision. One currency is designated as the base currency for the organization, serving as the default for reporting and consolidation.
6
+
7
+ Examples: USD (US Dollar), EUR (Euro), JPY (Japanese Yen).
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ Stateful
14
+
15
+ #### State Transitions
16
+
17
+ ```mermaid
18
+ stateDiagram-v2
19
+ [*] --> Active: create
20
+ Active --> Inactive: deactivate
21
+ Inactive --> Active: activate
22
+ note right of Active: Base currency cannot be deactivated
23
+ ```
24
+
25
+ ### Command Definitions
26
+
27
+ - [createCurrency](../commands/CreateCurrency.md)
28
+ - [activateCurrency](../commands/ActivateCurrency.md)
29
+ - [deactivateCurrency](../commands/DeactivateCurrency.md)
30
+ - [setBaseCurrency](../commands/SetBaseCurrency.md)
31
+
32
+ ### Models
33
+
34
+ - Currency
35
+
36
+ ### Invariants
37
+
38
+ - ISO 4217 code must be unique across all currencies
39
+ - Only one currency can be designated as base currency
40
+ - Base currency cannot be deactivated
41
+
42
+ ### Relationships
43
+
44
+ - **Referenced By Exchange Rates**: Currency is referenced as source or target in ExchangeRate records
45
+ - **Referenced By Transactions**: Currency is used in sales, purchase, and accounting transactions
@@ -0,0 +1,33 @@
1
+ # ExchangeRate
2
+
3
+ ## Description
4
+
5
+ ExchangeRate maintains the conversion ratio between a currency pair with an effective date. Each rate record specifies the source currency, target currency, rate value, and the date from which it applies. The system uses these rates to convert monetary amounts for transactions, reporting, and consolidation.
6
+
7
+ Rates are date-based to support historical accuracy - a transaction from last month uses last month's rate, not today's rate.
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ AppendOnly
14
+
15
+ ### Command Definitions
16
+
17
+ - [createExchangeRate](../commands/CreateExchangeRate.md)
18
+ - [convertAmount](../commands/ConvertAmount.md)
19
+
20
+ ### Models
21
+
22
+ - ExchangeRate
23
+
24
+ ### Invariants
25
+
26
+ - Rate must be a positive decimal value
27
+ - Source and target currencies must be different
28
+ - Effective date is required for all rate records
29
+
30
+ ### Relationships
31
+
32
+ - **Belongs To Source Currency**: Each rate has exactly one source currency
33
+ - **Belongs To Target Currency**: Each rate has exactly one target currency
@@ -0,0 +1,46 @@
1
+ # Unit
2
+
3
+ ## Description
4
+
5
+ Unit defines an individual unit of measure with its symbol, display name, and conversion factor relative to its category's reference unit. Units support configurable rounding precision for business operations.
6
+
7
+ Examples: kg (kilogram), lb (pound), ea (each), dz (dozen).
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ Stateful
14
+
15
+ #### State Transitions
16
+
17
+ ```mermaid
18
+ stateDiagram-v2
19
+ [*] --> Active: create
20
+ Active --> Inactive: deactivate
21
+ Inactive --> Active: activate
22
+ note right of Active: Reference unit cannot be deactivated
23
+ ```
24
+
25
+ ### Command Definitions
26
+
27
+ - [createUnit](../commands/CreateUnit.md)
28
+ - [activateUnit](../commands/ActivateUnit.md)
29
+ - [deactivateUnit](../commands/DeactivateUnit.md)
30
+ - [convertQuantity](../commands/ConvertQuantity.md)
31
+
32
+ ### Models
33
+
34
+ - Unit
35
+
36
+ ### Invariants
37
+
38
+ - Conversion factor must be a positive value
39
+ - Reference unit has conversion factor of 1.0
40
+ - Symbol must be unique within the category
41
+ - Reference unit cannot be deactivated
42
+
43
+ ### Relationships
44
+
45
+ - **Belongs To Category**: Each unit belongs to exactly one UoMCategory
46
+ - **May Be Reference Unit**: A unit may be designated as its category's reference unit
@@ -0,0 +1,44 @@
1
+ # UoMCategory
2
+
3
+ ## Description
4
+
5
+ UoMCategory organizes units of measure into logical groupings where conversions are meaningful. Each category has a designated reference unit that serves as the base for all conversion calculations within that category. Units can only be converted to other units within the same category.
6
+
7
+ Examples: Unit, Weight, Volume, Length, Time.
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ Stateful
14
+
15
+ #### State Transitions
16
+
17
+ ```mermaid
18
+ stateDiagram-v2
19
+ [*] --> Active: create
20
+ Active --> Inactive: deactivate
21
+ Inactive --> Active: activate
22
+ ```
23
+
24
+ ### Command Definitions
25
+
26
+ - [createCategory](../commands/CreateCategory.md)
27
+ - [activateCategory](../commands/ActivateCategory.md)
28
+ - [deactivateCategory](../commands/DeactivateCategory.md)
29
+ - [setReferenceUnit](../commands/SetReferenceUnit.md)
30
+
31
+ ### Models
32
+
33
+ - UoMCategory
34
+
35
+ ### Invariants
36
+
37
+ - Category name must be unique
38
+ - Reference unit must belong to this category
39
+ - Category cannot be deactivated while it has active units
40
+
41
+ ### Relationships
42
+
43
+ - **Has Many Units**: A category contains multiple units (one-to-many)
44
+ - **Has One Reference Unit**: Each category designates one unit as the reference unit for conversions
@@ -0,0 +1,95 @@
1
+ import {
2
+ type ColumnType,
3
+ Kysely,
4
+ type KyselyConfig,
5
+ type Transaction as KyselyTransaction,
6
+ type Insertable as KyselyInsertable,
7
+ type Selectable as KyselySelectable,
8
+ type Updateable as KyselyUpdateable,
9
+ } from "kysely";
10
+ import { TailordbDialect } from "@tailor-platform/function-kysely-tailordb";
11
+
12
+ type Timestamp = ColumnType<Date, Date | string, Date | string>;
13
+ type Generated<T> =
14
+ T extends ColumnType<infer S, infer I, infer U>
15
+ ? ColumnType<S, I | undefined, U>
16
+ : ColumnType<T, T | undefined, T>;
17
+
18
+ export interface Namespace {
19
+ "main-db": {
20
+ Currency: {
21
+ id: Generated<string>;
22
+ code: string;
23
+ name: string;
24
+ symbol: string;
25
+ decimalPlaces: number;
26
+ isBaseCurrency: boolean;
27
+ isActive: boolean;
28
+ createdAt: Generated<Timestamp>;
29
+ updatedAt: Timestamp | null;
30
+ };
31
+
32
+ ExchangeRate: {
33
+ id: Generated<string>;
34
+ sourceCurrencyId: string;
35
+ targetCurrencyId: string;
36
+ rate: number;
37
+ effectiveDate: Timestamp;
38
+ createdAt: Generated<Timestamp>;
39
+ updatedAt: Timestamp | null;
40
+ };
41
+
42
+ Unit: {
43
+ id: Generated<string>;
44
+ name: string;
45
+ symbol: string;
46
+ categoryId: string;
47
+ conversionFactor: number;
48
+ roundingPrecision: number;
49
+ isActive: boolean;
50
+ createdAt: Generated<Timestamp>;
51
+ updatedAt: Timestamp | null;
52
+ };
53
+
54
+ UoMCategory: {
55
+ id: Generated<string>;
56
+ name: string;
57
+ description: string | null;
58
+ referenceUnitId: string | null;
59
+ isActive: boolean;
60
+ createdAt: Generated<Timestamp>;
61
+ updatedAt: Timestamp | null;
62
+ };
63
+ };
64
+ }
65
+
66
+ export function getDB<const N extends keyof Namespace>(
67
+ namespace: N,
68
+ kyselyConfig?: Omit<KyselyConfig, "dialect">,
69
+ ): Kysely<Namespace[N]> {
70
+ const client = new tailordb.Client({ namespace });
71
+ return new Kysely<Namespace[N]>({
72
+ dialect: new TailordbDialect(client),
73
+ ...kyselyConfig,
74
+ });
75
+ }
76
+
77
+ export type DB<N extends keyof Namespace = keyof Namespace> = ReturnType<typeof getDB<N>>;
78
+
79
+ export type Transaction<K extends keyof Namespace | DB = keyof Namespace> =
80
+ K extends DB<infer N>
81
+ ? KyselyTransaction<Namespace[N]>
82
+ : K extends keyof Namespace
83
+ ? KyselyTransaction<Namespace[K]>
84
+ : never;
85
+
86
+ type TableName = {
87
+ [N in keyof Namespace]: keyof Namespace[N];
88
+ }[keyof Namespace];
89
+ export type Table<T extends TableName> = {
90
+ [N in keyof Namespace]: T extends keyof Namespace[N] ? Namespace[N][T] : never;
91
+ }[keyof Namespace];
92
+
93
+ export type Insertable<T extends keyof Namespace[keyof Namespace]> = KyselyInsertable<Table<T>>;
94
+ export type Selectable<T extends keyof Namespace[keyof Namespace]> = KyselySelectable<Table<T>>;
95
+ export type Updateable<T extends keyof Namespace[keyof Namespace]> = KyselyUpdateable<Table<T>>;
@@ -0,0 +1,40 @@
1
+ export { defineModule } from "./module";
2
+ export { permissions, own, all } from "./permissions";
3
+
4
+ // errors
5
+ export {
6
+ UoMCategoryNotFoundError,
7
+ UnitNotFoundError,
8
+ IncompatibleUnitsError,
9
+ InactiveUnitError,
10
+ CurrencyNotFoundError,
11
+ InactiveCurrencyError,
12
+ ExchangeRateNotFoundError,
13
+ CannotDeactivateReferenceUnitError,
14
+ CategoryNotActiveError,
15
+ DuplicateUnitSymbolError,
16
+ InvalidConversionFactorError,
17
+ InvalidRoundingPrecisionError,
18
+ DuplicateCategoryNameError,
19
+ CategoryHasActiveUnitsError,
20
+ UnitNotInCategoryError,
21
+ InvalidISOCodeError,
22
+ DuplicateCurrencyCodeError,
23
+ InvalidDecimalPlacesError,
24
+ CannotDeactivateBaseCurrencyError,
25
+ CannotSetInactiveAsBaseCurrencyError,
26
+ SameCurrencyPairError,
27
+ InvalidExchangeRateError,
28
+ } from "./lib/errors";
29
+
30
+ // input types
31
+ export { type ConvertQuantityInput } from "./command/convertQuantity";
32
+ export { type ActivateCategoryInput } from "./command/activateCategory";
33
+ export { type DeactivateCategoryInput } from "./command/deactivateCategory";
34
+ export { type SetReferenceUnitInput } from "./command/setReferenceUnit";
35
+ export { type ActivateUnitInput } from "./command/activateUnit";
36
+ export { type DeactivateUnitInput } from "./command/deactivateUnit";
37
+ export { type ConvertAmountInput } from "./command/convertAmount";
38
+ export { type ActivateCurrencyInput } from "./command/activateCurrency";
39
+ export { type DeactivateCurrencyInput } from "./command/deactivateCurrency";
40
+ export { type SetBaseCurrencyInput } from "./command/setBaseCurrency";
@@ -0,0 +1,138 @@
1
+ import { createDomainError } from "../../shared/internal";
2
+
3
+ export const UoMCategoryNotFoundError = createDomainError(
4
+ "UoMCategoryNotFoundError",
5
+ "UOM_CATEGORY_NOT_FOUND",
6
+ (identifier: string) => `UoM category not found: ${identifier}`,
7
+ );
8
+
9
+ export const UnitNotFoundError = createDomainError(
10
+ "UnitNotFoundError",
11
+ "UNIT_NOT_FOUND",
12
+ (symbol: string) => `Unit not found: ${symbol}`,
13
+ );
14
+
15
+ export const IncompatibleUnitsError = createDomainError(
16
+ "IncompatibleUnitsError",
17
+ "INCOMPATIBLE_UNITS",
18
+ (sourceSymbol: string, targetSymbol: string) =>
19
+ `Cannot convert between incompatible units: ${sourceSymbol} and ${targetSymbol} belong to different categories`,
20
+ );
21
+
22
+ export const InactiveUnitError = createDomainError(
23
+ "InactiveUnitError",
24
+ "INACTIVE_UNIT",
25
+ (symbol: string) => `Unit is inactive: ${symbol}`,
26
+ );
27
+
28
+ export const CurrencyNotFoundError = createDomainError(
29
+ "CurrencyNotFoundError",
30
+ "CURRENCY_NOT_FOUND",
31
+ (code: string) => `Currency not found: ${code}`,
32
+ );
33
+
34
+ export const InactiveCurrencyError = createDomainError(
35
+ "InactiveCurrencyError",
36
+ "INACTIVE_CURRENCY",
37
+ (code: string) => `Currency is inactive: ${code}`,
38
+ );
39
+
40
+ export const ExchangeRateNotFoundError = createDomainError(
41
+ "ExchangeRateNotFoundError",
42
+ "EXCHANGE_RATE_NOT_FOUND",
43
+ (sourceCode: string, targetCode: string, date: string) =>
44
+ `No exchange rate found for ${sourceCode} to ${targetCode} on or before ${date}`,
45
+ );
46
+
47
+ export const CannotDeactivateReferenceUnitError = createDomainError(
48
+ "CannotDeactivateReferenceUnitError",
49
+ "CANNOT_DEACTIVATE_REFERENCE_UNIT",
50
+ (unitId: string) => `Cannot deactivate reference unit: ${unitId}. Change reference unit first.`,
51
+ );
52
+
53
+ export const CategoryNotActiveError = createDomainError(
54
+ "CategoryNotActiveError",
55
+ "CATEGORY_NOT_ACTIVE",
56
+ (categoryId: string) => `Category is not active: ${categoryId}`,
57
+ );
58
+
59
+ export const DuplicateUnitSymbolError = createDomainError(
60
+ "DuplicateUnitSymbolError",
61
+ "DUPLICATE_UNIT_SYMBOL",
62
+ (symbol: string, categoryId: string) =>
63
+ `Unit with symbol "${symbol}" already exists in category ${categoryId}`,
64
+ );
65
+
66
+ export const InvalidConversionFactorError = createDomainError(
67
+ "InvalidConversionFactorError",
68
+ "INVALID_CONVERSION_FACTOR",
69
+ (factor: number) => `Conversion factor must be positive: ${factor}`,
70
+ );
71
+
72
+ export const InvalidRoundingPrecisionError = createDomainError(
73
+ "InvalidRoundingPrecisionError",
74
+ "INVALID_ROUNDING_PRECISION",
75
+ (precision: number) => `Rounding precision must be non-negative: ${precision}`,
76
+ );
77
+
78
+ export const DuplicateCategoryNameError = createDomainError(
79
+ "DuplicateCategoryNameError",
80
+ "DUPLICATE_CATEGORY_NAME",
81
+ (name: string) => `Category with name "${name}" already exists`,
82
+ );
83
+
84
+ export const CategoryHasActiveUnitsError = createDomainError(
85
+ "CategoryHasActiveUnitsError",
86
+ "CATEGORY_HAS_ACTIVE_UNITS",
87
+ (categoryId: string) => `Cannot deactivate category ${categoryId}: has active units`,
88
+ );
89
+
90
+ export const UnitNotInCategoryError = createDomainError(
91
+ "UnitNotInCategoryError",
92
+ "UNIT_NOT_IN_CATEGORY",
93
+ (unitId: string, categoryId: string) =>
94
+ `Unit ${unitId} does not belong to category ${categoryId}`,
95
+ );
96
+
97
+ export const InvalidISOCodeError = createDomainError(
98
+ "InvalidISOCodeError",
99
+ "INVALID_ISO_CODE",
100
+ (code: string) => `Invalid ISO 4217 code: "${code}". Must be exactly 3 uppercase letters.`,
101
+ );
102
+
103
+ export const DuplicateCurrencyCodeError = createDomainError(
104
+ "DuplicateCurrencyCodeError",
105
+ "DUPLICATE_CURRENCY_CODE",
106
+ (code: string) => `Currency with code "${code}" already exists`,
107
+ );
108
+
109
+ export const InvalidDecimalPlacesError = createDomainError(
110
+ "InvalidDecimalPlacesError",
111
+ "INVALID_DECIMAL_PLACES",
112
+ (decimalPlaces: number) => `Decimal places must be between 0 and 4: ${decimalPlaces}`,
113
+ );
114
+
115
+ export const CannotDeactivateBaseCurrencyError = createDomainError(
116
+ "CannotDeactivateBaseCurrencyError",
117
+ "CANNOT_DEACTIVATE_BASE_CURRENCY",
118
+ (currencyId: string) =>
119
+ `Cannot deactivate base currency: ${currencyId}. Change base currency first.`,
120
+ );
121
+
122
+ export const CannotSetInactiveAsBaseCurrencyError = createDomainError(
123
+ "CannotSetInactiveAsBaseCurrencyError",
124
+ "CANNOT_SET_INACTIVE_AS_BASE_CURRENCY",
125
+ (currencyId: string) => `Cannot set inactive currency as base: ${currencyId}. Activate it first.`,
126
+ );
127
+
128
+ export const SameCurrencyPairError = createDomainError(
129
+ "SameCurrencyPairError",
130
+ "SAME_CURRENCY_PAIR",
131
+ (currencyId: string) => `Source and target currency cannot be the same: ${currencyId}`,
132
+ );
133
+
134
+ export const InvalidExchangeRateError = createDomainError(
135
+ "InvalidExchangeRateError",
136
+ "INVALID_EXCHANGE_RATE",
137
+ (rate: number) => `Exchange rate must be positive: ${rate}`,
138
+ );
@@ -0,0 +1,20 @@
1
+ import type { InferSchema, Selectable, Insertable, Updateable } from "../../shared/internal";
2
+ import type { DB } from "../generated/kysely-tailordb";
3
+
4
+ export type Schema = InferSchema<DB>;
5
+
6
+ export type UoMCategory<T extends { UoMCategory: object }> = Selectable<T["UoMCategory"]>;
7
+ export type UoMCategoryCreate<T extends { UoMCategory: object }> = Insertable<T["UoMCategory"]>;
8
+ export type UoMCategoryUpdate<T extends { UoMCategory: object }> = Updateable<T["UoMCategory"]>;
9
+
10
+ export type Unit<T extends { Unit: object }> = Selectable<T["Unit"]>;
11
+ export type UnitCreate<T extends { Unit: object }> = Insertable<T["Unit"]>;
12
+ export type UnitUpdate<T extends { Unit: object }> = Updateable<T["Unit"]>;
13
+
14
+ export type Currency<T extends { Currency: object }> = Selectable<T["Currency"]>;
15
+ export type CurrencyCreate<T extends { Currency: object }> = Insertable<T["Currency"]>;
16
+ export type CurrencyUpdate<T extends { Currency: object }> = Updateable<T["Currency"]>;
17
+
18
+ export type ExchangeRate<T extends { ExchangeRate: object }> = Selectable<T["ExchangeRate"]>;
19
+ export type ExchangeRateCreate<T extends { ExchangeRate: object }> = Insertable<T["ExchangeRate"]>;
20
+ export type ExchangeRateUpdate<T extends { ExchangeRate: object }> = Updateable<T["ExchangeRate"]>;