@tailor-platform/erp-kit 0.1.1 → 0.2.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 +12 -0
- package/README.md +158 -62
- package/dist/cli.js +1010 -270
- package/package.json +11 -8
- package/schemas/module/command.yml +1 -0
- package/schemas/module/model.yml +14 -0
- package/schemas/module/query.yml +53 -0
- package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/SKILL.md +2 -2
- package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/SKILL.md +3 -3
- package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/SKILL.md +2 -2
- package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/SKILL.md +3 -3
- package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/SKILL.md +4 -4
- package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/SKILL.md +3 -3
- package/skills/{mock-scenario → erp-kit-mock-scenario}/SKILL.md +1 -1
- package/skills/{1-module-docs → erp-kit-module-1-docs}/SKILL.md +2 -2
- package/skills/{2-module-feature-breakdown → erp-kit-module-2-feature-breakdown}/SKILL.md +13 -9
- package/skills/erp-kit-module-2-feature-breakdown/references/naming.md +59 -0
- package/skills/{3-module-doc-review → erp-kit-module-3-doc-review}/SKILL.md +83 -25
- package/skills/erp-kit-module-4-tdd/SKILL.md +94 -0
- package/skills/erp-kit-module-4-tdd/references/cross-module-dependency.md +133 -0
- package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/db-relations.md +5 -1
- package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/exports.md +1 -1
- package/skills/erp-kit-module-4-tdd/references/generated-code.md +32 -0
- package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/SKILL.md +46 -44
- package/skills/erp-kit-module-5-impl-review/references/commands.md +62 -0
- package/skills/erp-kit-module-5-impl-review/references/errors.md +10 -0
- package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/references/testing.md +1 -1
- package/skills/erp-kit-module-shared/SKILL.md +16 -0
- package/skills/erp-kit-module-shared/references/commands.md +203 -0
- package/skills/erp-kit-module-shared/references/errors.md +35 -0
- package/skills/erp-kit-module-shared/references/queries.md +168 -0
- package/skills/erp-kit-module-shared/references/structure.md +36 -0
- package/skills/{3-module-doc-review → erp-kit-module-shared}/references/testing.md +4 -3
- package/skills/erp-kit-update/SKILL.md +64 -0
- package/src/cli.doc.test.ts +65 -0
- package/src/cli.ts +3 -117
- package/src/commands/app/index.ts +74 -0
- package/src/commands/check.test.ts +3 -2
- package/src/commands/check.ts +3 -2
- package/src/commands/index.ts +73 -0
- package/src/commands/init.test.ts +22 -5
- package/src/commands/init.ts +25 -16
- package/src/commands/license.ts +193 -0
- package/src/commands/mock/index.ts +2 -2
- package/src/commands/mock/start.ts +1 -1
- package/src/commands/mock/validate.test.ts +1 -1
- package/src/commands/module/generate.ts +35 -0
- package/src/commands/module/index.ts +87 -0
- package/src/commands/module/list.test.ts +57 -0
- package/src/commands/module/list.ts +64 -0
- package/src/commands/scaffold-templates.ts +65 -0
- package/src/commands/scaffold.test.ts +97 -2
- package/src/commands/scaffold.ts +24 -3
- package/src/commands/sync-check.test.ts +88 -1
- package/src/commands/sync-check.ts +41 -2
- package/src/generator/generate-code.test.ts +200 -0
- package/src/generator/generate-code.ts +260 -0
- package/src/generator/parse-command-doc.test.ts +159 -0
- package/src/generator/parse-command-doc.ts +116 -0
- package/src/integration.test.ts +6 -8
- package/src/module.ts +10 -9
- package/src/modules/item-management/README.md +38 -0
- package/src/modules/item-management/command/activateItem.generated.ts +6 -0
- package/src/modules/item-management/command/activateItem.test.ts +76 -0
- package/src/modules/item-management/command/activateItem.ts +42 -0
- package/src/modules/item-management/command/assignItemToTaxonomy.generated.ts +6 -0
- package/src/modules/item-management/command/assignItemToTaxonomy.test.ts +88 -0
- package/src/modules/item-management/command/assignItemToTaxonomy.ts +63 -0
- package/src/modules/item-management/command/createItem.generated.ts +6 -0
- package/src/modules/item-management/command/createItem.test.ts +152 -0
- package/src/modules/item-management/command/createItem.ts +72 -0
- package/src/modules/item-management/command/createTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/createTaxonomyNode.test.ts +126 -0
- package/src/modules/item-management/command/createTaxonomyNode.ts +70 -0
- package/src/modules/item-management/command/deactivateItem.generated.ts +6 -0
- package/src/modules/item-management/command/deactivateItem.test.ts +76 -0
- package/src/modules/item-management/command/deactivateItem.ts +42 -0
- package/src/modules/item-management/command/deleteItem.generated.ts +6 -0
- package/src/modules/item-management/command/deleteItem.test.ts +61 -0
- package/src/modules/item-management/command/deleteItem.ts +38 -0
- package/src/modules/item-management/command/deleteTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/deleteTaxonomyNode.test.ts +73 -0
- package/src/modules/item-management/command/deleteTaxonomyNode.ts +50 -0
- package/src/modules/item-management/command/moveTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/moveTaxonomyNode.test.ts +136 -0
- package/src/modules/item-management/command/moveTaxonomyNode.ts +85 -0
- package/src/modules/item-management/command/reactivateItem.generated.ts +6 -0
- package/src/modules/item-management/command/reactivateItem.test.ts +76 -0
- package/src/modules/item-management/command/reactivateItem.ts +42 -0
- package/src/modules/item-management/command/removeItemFromTaxonomy.generated.ts +6 -0
- package/src/modules/item-management/command/removeItemFromTaxonomy.test.ts +43 -0
- package/src/modules/item-management/command/removeItemFromTaxonomy.ts +30 -0
- package/src/modules/item-management/command/updateItem.generated.ts +6 -0
- package/src/modules/item-management/command/updateItem.test.ts +178 -0
- package/src/modules/item-management/command/updateItem.ts +103 -0
- package/src/modules/item-management/command/updateTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/updateTaxonomyNode.test.ts +88 -0
- package/src/modules/item-management/command/updateTaxonomyNode.ts +62 -0
- package/src/modules/item-management/db/item.ts +47 -0
- package/src/modules/item-management/db/itemTaxonomyAssignment.ts +49 -0
- package/src/modules/item-management/db/taxonomyNode.ts +34 -0
- package/src/modules/item-management/docs/commands/ActivateItem.md +32 -0
- package/src/modules/item-management/docs/commands/AssignItemToTaxonomy.md +38 -0
- package/src/modules/item-management/docs/commands/CreateItem.md +44 -0
- package/src/modules/item-management/docs/commands/CreateTaxonomyNode.md +44 -0
- package/src/modules/item-management/docs/commands/DeactivateItem.md +34 -0
- package/src/modules/item-management/docs/commands/DeleteItem.md +35 -0
- package/src/modules/item-management/docs/commands/DeleteTaxonomyNode.md +39 -0
- package/src/modules/item-management/docs/commands/MoveTaxonomyNode.md +45 -0
- package/src/modules/item-management/docs/commands/ReactivateItem.md +34 -0
- package/src/modules/item-management/docs/commands/RemoveItemFromTaxonomy.md +30 -0
- package/src/modules/item-management/docs/commands/UpdateItem.md +55 -0
- package/src/modules/item-management/docs/commands/UpdateTaxonomyNode.md +36 -0
- package/src/modules/item-management/docs/features/item-lifecycle.md +60 -0
- package/src/modules/item-management/docs/features/item-taxonomy.md +65 -0
- package/src/modules/item-management/docs/models/ItemTaxonomyAssignment.md +36 -0
- package/src/modules/item-management/docs/models/TaxonomyNode.md +47 -0
- package/src/modules/item-management/docs/models/item.md +59 -0
- package/src/modules/item-management/docs/queries/CalculateNodeDepth.md +36 -0
- package/src/modules/item-management/docs/queries/CalculateSubtreeDepth.md +40 -0
- package/src/modules/item-management/docs/queries/DetectCircularReference.md +41 -0
- package/src/modules/item-management/docs/queries/GetItem.md +38 -0
- package/src/modules/item-management/docs/queries/GetItemTaxonomyAssignment.md +29 -0
- package/src/modules/item-management/docs/queries/GetTaxonomyNode.md +35 -0
- package/src/modules/item-management/docs/queries/GetTaxonomyNodeAssignments.md +29 -0
- package/src/modules/item-management/docs/queries/GetTaxonomyNodeChildren.md +29 -0
- package/src/modules/item-management/generated/enums.ts +9 -0
- package/src/modules/item-management/generated/kysely-tailordb.ts +62 -0
- package/src/modules/item-management/index.ts +53 -0
- package/src/modules/item-management/lib/_db_deps.ts +13 -0
- package/src/modules/item-management/lib/errors.generated.ts +117 -0
- package/src/modules/item-management/lib/permissions.generated.ts +17 -0
- package/src/modules/item-management/lib/types.ts +19 -0
- package/src/modules/item-management/module.ts +97 -0
- package/src/modules/item-management/query/calculateNodeDepth.generated.ts +5 -0
- package/src/modules/item-management/query/calculateNodeDepth.test.ts +56 -0
- package/src/modules/item-management/query/calculateNodeDepth.ts +28 -0
- package/src/modules/item-management/query/calculateSubtreeDepth.generated.ts +5 -0
- package/src/modules/item-management/query/calculateSubtreeDepth.test.ts +75 -0
- package/src/modules/item-management/query/calculateSubtreeDepth.ts +29 -0
- package/src/modules/item-management/query/detectCircularReference.generated.ts +5 -0
- package/src/modules/item-management/query/detectCircularReference.test.ts +61 -0
- package/src/modules/item-management/query/detectCircularReference.ts +32 -0
- package/src/modules/item-management/query/getItem.generated.ts +5 -0
- package/src/modules/item-management/query/getItem.test.ts +67 -0
- package/src/modules/item-management/query/getItem.ts +20 -0
- package/src/modules/item-management/query/getItemTaxonomyAssignment.generated.ts +5 -0
- package/src/modules/item-management/query/getItemTaxonomyAssignment.test.ts +25 -0
- package/src/modules/item-management/query/getItemTaxonomyAssignment.ts +18 -0
- package/src/modules/item-management/query/getTaxonomyNode.generated.ts +5 -0
- package/src/modules/item-management/query/getTaxonomyNode.test.ts +47 -0
- package/src/modules/item-management/query/getTaxonomyNode.ts +18 -0
- package/src/modules/item-management/query/getTaxonomyNodeAssignments.generated.ts +5 -0
- package/src/modules/item-management/query/getTaxonomyNodeAssignments.test.ts +25 -0
- package/src/modules/item-management/query/getTaxonomyNodeAssignments.ts +16 -0
- package/src/modules/item-management/query/getTaxonomyNodeChildren.generated.ts +5 -0
- package/src/modules/item-management/query/getTaxonomyNodeChildren.test.ts +34 -0
- package/src/modules/item-management/query/getTaxonomyNodeChildren.ts +16 -0
- package/src/modules/item-management/tailor.config.ts +11 -0
- package/src/modules/item-management/testing/fixtures.ts +81 -0
- package/src/modules/primitives/command/activateCategory.generated.ts +6 -0
- package/src/modules/primitives/command/activateCategory.test.ts +11 -29
- package/src/modules/primitives/command/activateCategory.ts +27 -34
- package/src/modules/primitives/command/activateCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/activateCurrency.test.ts +11 -29
- package/src/modules/primitives/command/activateCurrency.ts +27 -34
- package/src/modules/primitives/command/activateUnit.generated.ts +6 -0
- package/src/modules/primitives/command/activateUnit.test.ts +11 -15
- package/src/modules/primitives/command/activateUnit.ts +27 -34
- package/src/modules/primitives/command/createCategory.generated.ts +6 -0
- package/src/modules/primitives/command/createCategory.test.ts +27 -39
- package/src/modules/primitives/command/createCategory.ts +53 -62
- package/src/modules/primitives/command/createCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/createCurrency.test.ts +78 -71
- package/src/modules/primitives/command/createCurrency.ts +43 -48
- package/src/modules/primitives/command/createExchangeRate.generated.ts +6 -0
- package/src/modules/primitives/command/createExchangeRate.test.ts +101 -100
- package/src/modules/primitives/command/createExchangeRate.ts +50 -59
- package/src/modules/primitives/command/createUnit.generated.ts +6 -0
- package/src/modules/primitives/command/createUnit.test.ts +92 -95
- package/src/modules/primitives/command/createUnit.ts +54 -57
- package/src/modules/primitives/command/deactivateCategory.generated.ts +6 -0
- package/src/modules/primitives/command/deactivateCategory.test.ts +27 -28
- package/src/modules/primitives/command/deactivateCategory.ts +43 -50
- package/src/modules/primitives/command/deactivateCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/deactivateCurrency.test.ts +23 -38
- package/src/modules/primitives/command/deactivateCurrency.ts +31 -38
- package/src/modules/primitives/command/deactivateUnit.generated.ts +6 -0
- package/src/modules/primitives/command/deactivateUnit.test.ts +27 -23
- package/src/modules/primitives/command/deactivateUnit.ts +39 -49
- package/src/modules/primitives/command/setBaseCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/setBaseCurrency.test.ts +40 -33
- package/src/modules/primitives/command/setBaseCurrency.ts +43 -50
- package/src/modules/primitives/command/setReferenceUnit.generated.ts +6 -0
- package/src/modules/primitives/command/setReferenceUnit.test.ts +39 -35
- package/src/modules/primitives/command/setReferenceUnit.ts +46 -59
- package/src/modules/primitives/db/unit.ts +13 -3
- package/src/modules/primitives/docs/commands/ActivateCategory.md +1 -2
- package/src/modules/primitives/docs/commands/ActivateCurrency.md +1 -2
- package/src/modules/primitives/docs/commands/ActivateUnit.md +1 -2
- package/src/modules/primitives/docs/commands/CreateCategory.md +1 -4
- package/src/modules/primitives/docs/commands/CreateCurrency.md +3 -4
- package/src/modules/primitives/docs/commands/CreateExchangeRate.md +4 -5
- package/src/modules/primitives/docs/commands/CreateUnit.md +5 -5
- package/src/modules/primitives/docs/commands/DeactivateCategory.md +2 -3
- package/src/modules/primitives/docs/commands/DeactivateCurrency.md +2 -3
- package/src/modules/primitives/docs/commands/DeactivateUnit.md +2 -3
- package/src/modules/primitives/docs/commands/SetBaseCurrency.md +2 -3
- package/src/modules/primitives/docs/commands/SetReferenceUnit.md +2 -3
- package/src/modules/primitives/docs/models/Currency.md +4 -0
- package/src/modules/primitives/docs/models/ExchangeRate.md +4 -1
- package/src/modules/primitives/docs/models/Unit.md +4 -1
- package/src/modules/primitives/docs/models/UoMCategory.md +2 -0
- package/src/modules/primitives/docs/{commands → queries}/ConvertAmount.md +3 -5
- package/src/modules/primitives/docs/{commands → queries}/ConvertQuantity.md +3 -5
- package/src/modules/primitives/docs/queries/GetBaseCurrency.md +32 -0
- package/src/modules/primitives/docs/queries/GetCurrency.md +36 -0
- package/src/modules/primitives/docs/queries/GetUnit.md +36 -0
- package/src/modules/primitives/docs/queries/GetUoMCategory.md +36 -0
- package/src/modules/primitives/docs/queries/ListUnitsByCategory.md +26 -0
- package/src/modules/primitives/generated/kysely-tailordb.ts +24 -45
- package/src/modules/primitives/index.ts +17 -6
- package/src/modules/primitives/lib/errors.generated.ts +112 -0
- package/src/modules/primitives/{permissions.ts → lib/permissions.generated.ts} +9 -10
- package/src/modules/primitives/module.ts +39 -27
- package/src/modules/primitives/query/convertAmount.generated.ts +5 -0
- package/src/modules/primitives/{command → query}/convertAmount.test.ts +4 -21
- package/src/modules/primitives/query/convertAmount.ts +121 -0
- package/src/modules/primitives/query/convertQuantity.generated.ts +5 -0
- package/src/modules/primitives/{command → query}/convertQuantity.test.ts +8 -15
- package/src/modules/primitives/query/convertQuantity.ts +63 -0
- package/src/modules/primitives/query/getBaseCurrency.generated.ts +5 -0
- package/src/modules/primitives/query/getBaseCurrency.test.ts +28 -0
- package/src/modules/primitives/query/getBaseCurrency.ts +16 -0
- package/src/modules/primitives/query/getCurrency.generated.ts +5 -0
- package/src/modules/primitives/query/getCurrency.test.ts +47 -0
- package/src/modules/primitives/query/getCurrency.ts +18 -0
- package/src/modules/primitives/query/getUnit.generated.ts +5 -0
- package/src/modules/primitives/query/getUnit.test.ts +47 -0
- package/src/modules/primitives/query/getUnit.ts +18 -0
- package/src/modules/primitives/query/getUoMCategory.generated.ts +5 -0
- package/src/modules/primitives/query/getUoMCategory.test.ts +47 -0
- package/src/modules/primitives/query/getUoMCategory.ts +18 -0
- package/src/modules/primitives/query/listUnitsByCategory.generated.ts +5 -0
- package/src/modules/primitives/query/listUnitsByCategory.ts +16 -0
- package/src/modules/primitives/tailor.config.ts +3 -3
- package/src/modules/shared/defineCommand.test.ts +23 -10
- package/src/modules/shared/defineCommand.ts +23 -10
- package/src/modules/shared/defineQuery.test.ts +28 -0
- package/src/modules/shared/defineQuery.ts +16 -0
- package/src/modules/shared/internal.ts +3 -1
- package/src/modules/shared/requirePermission.test.ts +22 -21
- package/src/modules/shared/requirePermission.ts +8 -2
- package/src/modules/shared/result.ts +12 -0
- package/src/modules/shared/types.ts +8 -0
- package/src/modules/testing/index.ts +36 -11
- package/src/modules/user-management/command/activateUser.generated.ts +6 -0
- package/src/modules/user-management/command/activateUser.test.ts +27 -27
- package/src/modules/user-management/command/activateUser.ts +40 -48
- package/src/modules/user-management/command/assignPermissionToRole.generated.ts +6 -0
- package/src/modules/user-management/command/assignPermissionToRole.test.ts +42 -43
- package/src/modules/user-management/command/assignPermissionToRole.ts +59 -62
- package/src/modules/user-management/command/assignRoleToUser.generated.ts +6 -0
- package/src/modules/user-management/command/assignRoleToUser.test.ts +70 -63
- package/src/modules/user-management/command/assignRoleToUser.ts +63 -66
- package/src/modules/user-management/command/createPermission.generated.ts +6 -0
- package/src/modules/user-management/command/createPermission.test.ts +45 -38
- package/src/modules/user-management/command/createPermission.ts +42 -46
- package/src/modules/user-management/command/createRole.generated.ts +6 -0
- package/src/modules/user-management/command/createRole.test.ts +30 -29
- package/src/modules/user-management/command/createRole.ts +33 -33
- package/src/modules/user-management/command/createUser.generated.ts +6 -0
- package/src/modules/user-management/command/createUser.test.ts +64 -42
- package/src/modules/user-management/command/createUser.ts +54 -56
- package/src/modules/user-management/command/deactivateUser.generated.ts +6 -0
- package/src/modules/user-management/command/deactivateUser.test.ts +27 -27
- package/src/modules/user-management/command/deactivateUser.ts +40 -48
- package/src/modules/user-management/command/logAuditEvent.generated.ts +6 -0
- package/src/modules/user-management/command/logAuditEvent.test.ts +50 -42
- package/src/modules/user-management/command/logAuditEvent.ts +25 -28
- package/src/modules/user-management/command/reactivateUser.generated.ts +6 -0
- package/src/modules/user-management/command/reactivateUser.test.ts +31 -27
- package/src/modules/user-management/command/reactivateUser.ts +40 -48
- package/src/modules/user-management/command/revokePermissionFromRole.generated.ts +6 -0
- package/src/modules/user-management/command/revokePermissionFromRole.test.ts +52 -51
- package/src/modules/user-management/command/revokePermissionFromRole.ts +60 -57
- package/src/modules/user-management/command/revokeRoleFromUser.generated.ts +6 -0
- package/src/modules/user-management/command/revokeRoleFromUser.test.ts +53 -48
- package/src/modules/user-management/command/revokeRoleFromUser.ts +58 -57
- package/src/modules/user-management/docs/commands/CreatePermission.md +2 -2
- package/src/modules/user-management/docs/commands/CreateRole.md +1 -1
- package/src/modules/user-management/docs/models/AuditEvent.md +2 -0
- package/src/modules/user-management/docs/models/Permission.md +2 -0
- package/src/modules/user-management/docs/models/Role.md +2 -0
- package/src/modules/user-management/docs/models/RolePermission.md +2 -0
- package/src/modules/user-management/docs/models/User.md +2 -0
- package/src/modules/user-management/docs/models/UserRole.md +2 -0
- package/src/modules/user-management/generated/enums.ts +11 -11
- package/src/modules/user-management/generated/kysely-tailordb.ts +27 -56
- package/src/modules/user-management/index.ts +2 -2
- package/src/modules/user-management/lib/errors.generated.ts +67 -0
- package/src/modules/user-management/{permissions.ts → lib/permissions.generated.ts} +8 -7
- package/src/modules/user-management/module.ts +22 -22
- package/src/modules/user-management/tailor.config.ts +3 -3
- package/src/schemas.ts +2 -1
- package/skills/1-module-docs/references/structure.md +0 -22
- package/skills/2-module-feature-breakdown/references/commands.md +0 -48
- package/skills/2-module-feature-breakdown/references/structure.md +0 -22
- package/skills/3-module-doc-review/references/commands.md +0 -54
- package/skills/3-module-doc-review/references/models.md +0 -29
- package/skills/4-module-tdd-implementation/SKILL.md +0 -74
- package/skills/4-module-tdd-implementation/references/commands.md +0 -45
- package/skills/4-module-tdd-implementation/references/errors.md +0 -7
- package/skills/4-module-tdd-implementation/references/models.md +0 -30
- package/skills/4-module-tdd-implementation/references/structure.md +0 -22
- package/skills/4-module-tdd-implementation/references/testing.md +0 -37
- package/skills/5-module-implementation-review/references/commands.md +0 -45
- package/skills/5-module-implementation-review/references/errors.md +0 -7
- package/skills/5-module-implementation-review/references/exports.md +0 -8
- package/skills/5-module-implementation-review/references/models.md +0 -30
- package/src/modules/primitives/command/convertAmount.ts +0 -126
- package/src/modules/primitives/command/convertQuantity.ts +0 -73
- package/src/modules/primitives/lib/errors.ts +0 -138
- package/src/modules/user-management/lib/errors.ts +0 -81
- /package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/references/structure.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-detailview.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-form.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-listview.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/structure.md +0 -0
- /package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/references/structure.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/component.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-detailview.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-form.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-listview.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/structure.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/component.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-detailview.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-form.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-listview.md +0 -0
- /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/auth.md +0 -0
- /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/structure.md +0 -0
- /package/skills/{2-module-feature-breakdown → erp-kit-module-4-tdd}/references/models.md +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ListUnitsByCategory
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
ListUnitsByCategory retrieves all units belonging to a specific UoM category. Used to find related units within a category and to validate category constraints such as ensuring a category has units before deactivation.
|
|
6
|
+
|
|
7
|
+
## Business Rules
|
|
8
|
+
|
|
9
|
+
- Returns all units with the matching categoryId
|
|
10
|
+
- Returns empty array if no units match
|
|
11
|
+
|
|
12
|
+
## Process Flow
|
|
13
|
+
|
|
14
|
+
```mermaid
|
|
15
|
+
flowchart TD
|
|
16
|
+
A[Receive categoryId] --> B[SELECT from Unit where categoryId = input]
|
|
17
|
+
B --> C[Return units array]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## External Dependencies
|
|
21
|
+
|
|
22
|
+
- None
|
|
23
|
+
|
|
24
|
+
## Error Scenarios
|
|
25
|
+
|
|
26
|
+
- None
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type
|
|
5
|
-
type
|
|
6
|
-
type
|
|
7
|
-
type
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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>;
|
|
2
|
+
createGetDB,
|
|
3
|
+
type Generated,
|
|
4
|
+
type Timestamp,
|
|
5
|
+
type NamespaceDB,
|
|
6
|
+
type NamespaceInsertable,
|
|
7
|
+
type NamespaceSelectable,
|
|
8
|
+
type NamespaceTable,
|
|
9
|
+
type NamespaceTableName,
|
|
10
|
+
type NamespaceTransaction,
|
|
11
|
+
type NamespaceUpdateable,
|
|
12
|
+
} from "@tailor-platform/sdk/kysely";
|
|
17
13
|
|
|
18
14
|
export interface Namespace {
|
|
19
15
|
"main-db": {
|
|
@@ -27,7 +23,7 @@ export interface Namespace {
|
|
|
27
23
|
isActive: boolean;
|
|
28
24
|
createdAt: Generated<Timestamp>;
|
|
29
25
|
updatedAt: Timestamp | null;
|
|
30
|
-
}
|
|
26
|
+
}
|
|
31
27
|
|
|
32
28
|
ExchangeRate: {
|
|
33
29
|
id: Generated<string>;
|
|
@@ -37,7 +33,7 @@ export interface Namespace {
|
|
|
37
33
|
effectiveDate: Timestamp;
|
|
38
34
|
createdAt: Generated<Timestamp>;
|
|
39
35
|
updatedAt: Timestamp | null;
|
|
40
|
-
}
|
|
36
|
+
}
|
|
41
37
|
|
|
42
38
|
Unit: {
|
|
43
39
|
id: Generated<string>;
|
|
@@ -49,7 +45,7 @@ export interface Namespace {
|
|
|
49
45
|
isActive: boolean;
|
|
50
46
|
createdAt: Generated<Timestamp>;
|
|
51
47
|
updatedAt: Timestamp | null;
|
|
52
|
-
}
|
|
48
|
+
}
|
|
53
49
|
|
|
54
50
|
UoMCategory: {
|
|
55
51
|
id: Generated<string>;
|
|
@@ -59,37 +55,20 @@ export interface Namespace {
|
|
|
59
55
|
isActive: boolean;
|
|
60
56
|
createdAt: Generated<Timestamp>;
|
|
61
57
|
updatedAt: Timestamp | null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
64
60
|
}
|
|
65
61
|
|
|
66
|
-
export
|
|
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
|
-
}
|
|
62
|
+
export const getDB = createGetDB<Namespace>();
|
|
76
63
|
|
|
77
|
-
export type DB<N extends keyof Namespace = keyof Namespace> =
|
|
64
|
+
export type DB<N extends keyof Namespace = keyof Namespace> = NamespaceDB<Namespace, N>;
|
|
78
65
|
|
|
79
66
|
export type Transaction<K extends keyof Namespace | DB = keyof Namespace> =
|
|
80
|
-
|
|
81
|
-
? KyselyTransaction<Namespace[N]>
|
|
82
|
-
: K extends keyof Namespace
|
|
83
|
-
? KyselyTransaction<Namespace[K]>
|
|
84
|
-
: never;
|
|
67
|
+
NamespaceTransaction<Namespace, K>;
|
|
85
68
|
|
|
86
|
-
type TableName =
|
|
87
|
-
|
|
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];
|
|
69
|
+
type TableName = NamespaceTableName<Namespace>;
|
|
70
|
+
export type Table<T extends TableName> = NamespaceTable<Namespace, T>;
|
|
92
71
|
|
|
93
|
-
export type Insertable<T extends
|
|
94
|
-
export type Selectable<T extends
|
|
95
|
-
export type Updateable<T extends
|
|
72
|
+
export type Insertable<T extends TableName> = NamespaceInsertable<Namespace, T>;
|
|
73
|
+
export type Selectable<T extends TableName> = NamespaceSelectable<Namespace, T>;
|
|
74
|
+
export type Updateable<T extends TableName> = NamespaceUpdateable<Namespace, T>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { defineModule } from "./module";
|
|
2
|
-
export { permissions, own, all } from "./permissions";
|
|
2
|
+
export { permissions, own, all } from "./lib/permissions.generated";
|
|
3
3
|
|
|
4
4
|
// errors
|
|
5
5
|
export {
|
|
6
|
-
|
|
6
|
+
UomCategoryNotFoundError,
|
|
7
7
|
UnitNotFoundError,
|
|
8
8
|
IncompatibleUnitsError,
|
|
9
9
|
InactiveUnitError,
|
|
@@ -18,23 +18,34 @@ export {
|
|
|
18
18
|
DuplicateCategoryNameError,
|
|
19
19
|
CategoryHasActiveUnitsError,
|
|
20
20
|
UnitNotInCategoryError,
|
|
21
|
-
|
|
21
|
+
InvalidIsoCodeError,
|
|
22
22
|
DuplicateCurrencyCodeError,
|
|
23
23
|
InvalidDecimalPlacesError,
|
|
24
24
|
CannotDeactivateBaseCurrencyError,
|
|
25
25
|
CannotSetInactiveAsBaseCurrencyError,
|
|
26
26
|
SameCurrencyPairError,
|
|
27
27
|
InvalidExchangeRateError,
|
|
28
|
-
} from "./lib/errors";
|
|
28
|
+
} from "./lib/errors.generated";
|
|
29
|
+
|
|
30
|
+
// queries
|
|
31
|
+
export { getUnit } from "./query/getUnit.generated";
|
|
32
|
+
export { getCurrency } from "./query/getCurrency.generated";
|
|
33
|
+
export { getUoMCategory } from "./query/getUoMCategory.generated";
|
|
34
|
+
export { getBaseCurrency } from "./query/getBaseCurrency.generated";
|
|
35
|
+
export { listUnitsByCategory } from "./query/listUnitsByCategory.generated";
|
|
29
36
|
|
|
30
37
|
// input types
|
|
31
|
-
export { type
|
|
38
|
+
export { type GetUnitInput } from "./query/getUnit";
|
|
39
|
+
export { type GetCurrencyInput } from "./query/getCurrency";
|
|
40
|
+
export { type GetUoMCategoryInput } from "./query/getUoMCategory";
|
|
41
|
+
export { type ListUnitsByCategoryInput } from "./query/listUnitsByCategory";
|
|
42
|
+
export { type ConvertQuantityInput } from "./query/convertQuantity";
|
|
32
43
|
export { type ActivateCategoryInput } from "./command/activateCategory";
|
|
33
44
|
export { type DeactivateCategoryInput } from "./command/deactivateCategory";
|
|
34
45
|
export { type SetReferenceUnitInput } from "./command/setReferenceUnit";
|
|
35
46
|
export { type ActivateUnitInput } from "./command/activateUnit";
|
|
36
47
|
export { type DeactivateUnitInput } from "./command/deactivateUnit";
|
|
37
|
-
export { type ConvertAmountInput } from "./
|
|
48
|
+
export { type ConvertAmountInput } from "./query/convertAmount";
|
|
38
49
|
export { type ActivateCurrencyInput } from "./command/activateCurrency";
|
|
39
50
|
export { type DeactivateCurrencyInput } from "./command/deactivateCurrency";
|
|
40
51
|
export { type SetBaseCurrencyInput } from "./command/setBaseCurrency";
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// @generated — do not edit
|
|
2
|
+
import { createDomainError } from "../../shared/internal";
|
|
3
|
+
|
|
4
|
+
export const UomCategoryNotFoundError = createDomainError(
|
|
5
|
+
"UomCategoryNotFoundError", "UOM_CATEGORY_NOT_FOUND",
|
|
6
|
+
(identifier: string) => `Specified category ID does not exist: ${identifier}`,
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export const CurrencyNotFoundError = createDomainError(
|
|
10
|
+
"CurrencyNotFoundError", "CURRENCY_NOT_FOUND",
|
|
11
|
+
(identifier: string) => `Specified currency ID does not exist: ${identifier}`,
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export const UnitNotFoundError = createDomainError(
|
|
15
|
+
"UnitNotFoundError", "UNIT_NOT_FOUND",
|
|
16
|
+
(identifier: string) => `Specified unit ID does not exist: ${identifier}`,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export const DuplicateCategoryNameError = createDomainError(
|
|
20
|
+
"DuplicateCategoryNameError", "DUPLICATE_CATEGORY_NAME",
|
|
21
|
+
(identifier: string) => `Category with same name already exists: ${identifier}`,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const InvalidIsoCodeError = createDomainError(
|
|
25
|
+
"InvalidIsoCodeError", "INVALID_ISO_CODE",
|
|
26
|
+
(identifier: string) => `Code is not exactly 3 uppercase letters: ${identifier}`,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const DuplicateCurrencyCodeError = createDomainError(
|
|
30
|
+
"DuplicateCurrencyCodeError", "DUPLICATE_CURRENCY_CODE",
|
|
31
|
+
(identifier: string) => `Currency with same code already exists: ${identifier}`,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const InvalidDecimalPlacesError = createDomainError(
|
|
35
|
+
"InvalidDecimalPlacesError", "INVALID_DECIMAL_PLACES",
|
|
36
|
+
(identifier: string) => `Value is negative or exceeds maximum (typically 4): ${identifier}`,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const InactiveCurrencyError = createDomainError(
|
|
40
|
+
"InactiveCurrencyError", "INACTIVE_CURRENCY",
|
|
41
|
+
(identifier: string) => `Source or target currency is inactive: ${identifier}`,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const SameCurrencyPairError = createDomainError(
|
|
45
|
+
"SameCurrencyPairError", "SAME_CURRENCY_PAIR",
|
|
46
|
+
(identifier: string) => `Source and target currencies are the same: ${identifier}`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
export const InvalidExchangeRateError = createDomainError(
|
|
50
|
+
"InvalidExchangeRateError", "INVALID_EXCHANGE_RATE",
|
|
51
|
+
(identifier: string) => `Rate is zero, negative, or not a valid number: ${identifier}`,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export const CategoryNotActiveError = createDomainError(
|
|
55
|
+
"CategoryNotActiveError", "CATEGORY_NOT_ACTIVE",
|
|
56
|
+
(identifier: string) => `Category is inactive: ${identifier}`,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
export const DuplicateUnitSymbolError = createDomainError(
|
|
60
|
+
"DuplicateUnitSymbolError", "DUPLICATE_UNIT_SYMBOL",
|
|
61
|
+
(identifier: string) => `Unit with same symbol already exists in category: ${identifier}`,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
export const InvalidConversionFactorError = createDomainError(
|
|
65
|
+
"InvalidConversionFactorError", "INVALID_CONVERSION_FACTOR",
|
|
66
|
+
(identifier: string) => `Factor is zero, negative, or not a valid number: ${identifier}`,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
export const InvalidRoundingPrecisionError = createDomainError(
|
|
70
|
+
"InvalidRoundingPrecisionError", "INVALID_ROUNDING_PRECISION",
|
|
71
|
+
(identifier: string) => `Precision is negative: ${identifier}`,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
export const CategoryHasActiveUnitsError = createDomainError(
|
|
75
|
+
"CategoryHasActiveUnitsError", "CATEGORY_HAS_ACTIVE_UNITS",
|
|
76
|
+
(identifier: string) => `Category still contains active units that must be deactivated first: ${identifier}`,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
export const CannotDeactivateBaseCurrencyError = createDomainError(
|
|
80
|
+
"CannotDeactivateBaseCurrencyError", "CANNOT_DEACTIVATE_BASE_CURRENCY",
|
|
81
|
+
(identifier: string) => `Attempting to deactivate the base currency, must change base currency first: ${identifier}`,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
export const CannotDeactivateReferenceUnitError = createDomainError(
|
|
85
|
+
"CannotDeactivateReferenceUnitError", "CANNOT_DEACTIVATE_REFERENCE_UNIT",
|
|
86
|
+
(identifier: string) => `Attempting to deactivate the category's reference unit, must change reference unit first: ${identifier}`,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
export const CannotSetInactiveAsBaseCurrencyError = createDomainError(
|
|
90
|
+
"CannotSetInactiveAsBaseCurrencyError", "CANNOT_SET_INACTIVE_AS_BASE_CURRENCY",
|
|
91
|
+
(identifier: string) => `Target currency is not in Active status, must be activated first: ${identifier}`,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
export const UnitNotInCategoryError = createDomainError(
|
|
95
|
+
"UnitNotInCategoryError", "UNIT_NOT_IN_CATEGORY",
|
|
96
|
+
(identifier: string) => `Unit belongs to a different category than specified: ${identifier}`,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
export const ExchangeRateNotFoundError = createDomainError(
|
|
100
|
+
"ExchangeRateNotFoundError", "EXCHANGE_RATE_NOT_FOUND",
|
|
101
|
+
(identifier: string) => `No rate found for the currency pair on or before the specified date: ${identifier}`,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
export const IncompatibleUnitsError = createDomainError(
|
|
105
|
+
"IncompatibleUnitsError", "INCOMPATIBLE_UNITS",
|
|
106
|
+
(identifier: string) => `Source and target units belong to different categories: ${identifier}`,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
export const InactiveUnitError = createDomainError(
|
|
110
|
+
"InactiveUnitError", "INACTIVE_UNIT",
|
|
111
|
+
(identifier: string) => `Either source or target unit is inactive: ${identifier}`,
|
|
112
|
+
);
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
// @generated — do not edit
|
|
2
|
+
import { definePermissions } from "../../shared/internal";
|
|
2
3
|
|
|
3
4
|
export const { permissions, own, all } = definePermissions("primitives", [
|
|
4
|
-
"convertQuantity",
|
|
5
|
-
"createCategory",
|
|
6
5
|
"activateCategory",
|
|
7
|
-
"
|
|
8
|
-
"setReferenceUnit",
|
|
9
|
-
"createUnit",
|
|
6
|
+
"activateCurrency",
|
|
10
7
|
"activateUnit",
|
|
11
|
-
"
|
|
12
|
-
"convertAmount",
|
|
8
|
+
"createCategory",
|
|
13
9
|
"createCurrency",
|
|
14
|
-
"
|
|
10
|
+
"createExchangeRate",
|
|
11
|
+
"createUnit",
|
|
12
|
+
"deactivateCategory",
|
|
15
13
|
"deactivateCurrency",
|
|
14
|
+
"deactivateUnit",
|
|
16
15
|
"setBaseCurrency",
|
|
17
|
-
"
|
|
16
|
+
"setReferenceUnit",
|
|
18
17
|
] as const);
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { type TailorAnyDBField } from "@tailor-platform/sdk";
|
|
2
2
|
import { type EmptyFields, type FieldsToInsertable } from "../shared/internal";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { activateCategory } from "./command/activateCategory";
|
|
8
|
-
import { deactivateCategory } from "./command/deactivateCategory";
|
|
9
|
-
import { activateUnit } from "./command/activateUnit";
|
|
10
|
-
import { deactivateUnit } from "./command/deactivateUnit";
|
|
11
|
-
import { setReferenceUnit } from "./command/setReferenceUnit";
|
|
12
|
-
import { convertQuantity } from "./
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
3
|
+
import { createCategory } from "./command/createCategory.generated";
|
|
4
|
+
import { createCurrency } from "./command/createCurrency.generated";
|
|
5
|
+
import { createUnit } from "./command/createUnit.generated";
|
|
6
|
+
import { createExchangeRate } from "./command/createExchangeRate.generated";
|
|
7
|
+
import { activateCategory } from "./command/activateCategory.generated";
|
|
8
|
+
import { deactivateCategory } from "./command/deactivateCategory.generated";
|
|
9
|
+
import { activateUnit } from "./command/activateUnit.generated";
|
|
10
|
+
import { deactivateUnit } from "./command/deactivateUnit.generated";
|
|
11
|
+
import { setReferenceUnit } from "./command/setReferenceUnit.generated";
|
|
12
|
+
import { convertQuantity } from "./query/convertQuantity.generated";
|
|
13
|
+
import { getUnit } from "./query/getUnit.generated";
|
|
14
|
+
import { getCurrency } from "./query/getCurrency.generated";
|
|
15
|
+
import { getUoMCategory } from "./query/getUoMCategory.generated";
|
|
16
|
+
import { getBaseCurrency } from "./query/getBaseCurrency.generated";
|
|
17
|
+
import { listUnitsByCategory } from "./query/listUnitsByCategory.generated";
|
|
18
|
+
import { activateCurrency } from "./command/activateCurrency.generated";
|
|
19
|
+
import { deactivateCurrency } from "./command/deactivateCurrency.generated";
|
|
20
|
+
import { setBaseCurrency } from "./command/setBaseCurrency.generated";
|
|
21
|
+
import { convertAmount } from "./query/convertAmount.generated";
|
|
17
22
|
import { createUoMCategoryType, CreateUoMCategoryTypeParams } from "./db/uomCategory";
|
|
18
23
|
import { createUnitType, CreateUnitTypeParams } from "./db/unit";
|
|
19
24
|
import { createCurrencyType, CreateCurrencyTypeParams } from "./db/currency";
|
|
@@ -40,26 +45,33 @@ export const defineModule = <
|
|
|
40
45
|
params: DefineModuleParams<CatF, UnitF, CurF, ERF>,
|
|
41
46
|
) => {
|
|
42
47
|
const uomCategory = createUoMCategoryType(params.uomCategory ?? {});
|
|
43
|
-
const unit = createUnitType(params.unit
|
|
48
|
+
const unit = createUnitType({ ...params.unit, uomCategoryType: uomCategory });
|
|
44
49
|
const currency = createCurrencyType(params.currency ?? {});
|
|
45
50
|
const exchangeRate = createExchangeRateType(params.exchangeRate ?? {});
|
|
46
51
|
|
|
47
52
|
return {
|
|
48
53
|
db: { uomCategory, unit, currency, exchangeRate },
|
|
49
54
|
commands: {
|
|
50
|
-
createCategory:
|
|
51
|
-
createCurrency:
|
|
52
|
-
createUnit:
|
|
53
|
-
createExchangeRate:
|
|
54
|
-
activateCategory,
|
|
55
|
-
deactivateCategory,
|
|
56
|
-
activateUnit,
|
|
57
|
-
deactivateUnit,
|
|
58
|
-
setReferenceUnit,
|
|
55
|
+
createCategory: createCategory<FieldsToInsertable<CatF>, FieldsToInsertable<UnitF>>(),
|
|
56
|
+
createCurrency: createCurrency<FieldsToInsertable<CurF>>(),
|
|
57
|
+
createUnit: createUnit<FieldsToInsertable<UnitF>>(),
|
|
58
|
+
createExchangeRate: createExchangeRate<FieldsToInsertable<ERF>>(),
|
|
59
|
+
activateCategory: activateCategory(),
|
|
60
|
+
deactivateCategory: deactivateCategory(),
|
|
61
|
+
activateUnit: activateUnit(),
|
|
62
|
+
deactivateUnit: deactivateUnit(),
|
|
63
|
+
setReferenceUnit: setReferenceUnit(),
|
|
64
|
+
activateCurrency: activateCurrency(),
|
|
65
|
+
deactivateCurrency: deactivateCurrency(),
|
|
66
|
+
setBaseCurrency: setBaseCurrency(),
|
|
67
|
+
},
|
|
68
|
+
queries: {
|
|
69
|
+
getUnit,
|
|
70
|
+
getCurrency,
|
|
71
|
+
getUoMCategory,
|
|
72
|
+
getBaseCurrency,
|
|
73
|
+
listUnitsByCategory,
|
|
59
74
|
convertQuantity,
|
|
60
|
-
activateCurrency,
|
|
61
|
-
deactivateCurrency,
|
|
62
|
-
setBaseCurrency,
|
|
63
75
|
convertAmount,
|
|
64
76
|
},
|
|
65
77
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { InsufficientPermissionError, type CommandContext } from "../../shared/internal";
|
|
3
2
|
import { createMockDb } from "../../testing/index";
|
|
4
3
|
import { DB } from "../generated/kysely-tailordb";
|
|
4
|
+
import type { QueryContext } from "../../shared/internal";
|
|
5
5
|
import {
|
|
6
6
|
CurrencyNotFoundError,
|
|
7
7
|
ExchangeRateNotFoundError,
|
|
8
8
|
InactiveCurrencyError,
|
|
9
|
-
} from "../lib/errors";
|
|
9
|
+
} from "../lib/errors.generated";
|
|
10
10
|
import {
|
|
11
11
|
baseCurrencyEUR,
|
|
12
12
|
baseCurrencyJPY,
|
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
inactiveCurrency,
|
|
17
17
|
olderExchangeRateUSDtoEUR,
|
|
18
18
|
} from "../testing/fixtures";
|
|
19
|
-
import { convertAmount } from "./convertAmount";
|
|
19
|
+
import { convertAmount } from "./convertAmount.generated";
|
|
20
20
|
|
|
21
21
|
describe("convertAmount", () => {
|
|
22
|
-
const ctx:
|
|
22
|
+
const ctx: QueryContext = { actorId: "test-actor" };
|
|
23
23
|
|
|
24
24
|
// Error cases first
|
|
25
25
|
it("throws when source currency doesn't exist", async () => {
|
|
@@ -255,21 +255,4 @@ describe("convertAmount", () => {
|
|
|
255
255
|
|
|
256
256
|
expect(result.convertedAmount).toBe(0);
|
|
257
257
|
});
|
|
258
|
-
|
|
259
|
-
it("throws when permission is missing", async () => {
|
|
260
|
-
const { db } = createMockDb<DB>();
|
|
261
|
-
const denied: CommandContext = { actorId: "test-actor", permissions: [] };
|
|
262
|
-
await expect(
|
|
263
|
-
convertAmount(
|
|
264
|
-
db,
|
|
265
|
-
{
|
|
266
|
-
amount: 100,
|
|
267
|
-
sourceCurrencyCode: "USD",
|
|
268
|
-
targetCurrencyCode: "EUR",
|
|
269
|
-
conversionDate: "2024-01-15",
|
|
270
|
-
},
|
|
271
|
-
denied,
|
|
272
|
-
),
|
|
273
|
-
).rejects.toBeInstanceOf(InsufficientPermissionError);
|
|
274
|
-
});
|
|
275
258
|
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ReadonlyDB, QueryContext } from "../../shared/internal";
|
|
2
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
3
|
+
import {
|
|
4
|
+
CurrencyNotFoundError,
|
|
5
|
+
ExchangeRateNotFoundError,
|
|
6
|
+
InactiveCurrencyError,
|
|
7
|
+
} from "../lib/errors.generated";
|
|
8
|
+
import { getCurrency } from "./getCurrency.generated";
|
|
9
|
+
|
|
10
|
+
export interface ConvertAmountInput {
|
|
11
|
+
amount: number;
|
|
12
|
+
sourceCurrencyCode: string;
|
|
13
|
+
targetCurrencyCode: string;
|
|
14
|
+
conversionDate: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Converts a monetary amount from one currency to another using the applicable
|
|
19
|
+
* exchange rate for a given date. Currencies are identified by their ISO 4217
|
|
20
|
+
* code (e.g., "USD", "EUR", "JPY"). The function looks up the most recent exchange
|
|
21
|
+
* rate on or before the specified date. If no direct rate exists, it calculates
|
|
22
|
+
* the inverse rate. Result is rounded to the target currency's decimal precision.
|
|
23
|
+
*/
|
|
24
|
+
export async function run(db: ReadonlyDB<DB>, input: ConvertAmountInput, ctx: QueryContext) {
|
|
25
|
+
// Validate source currency exists
|
|
26
|
+
const { currency: sourceCurrency } = await getCurrency(
|
|
27
|
+
db,
|
|
28
|
+
{
|
|
29
|
+
code: input.sourceCurrencyCode,
|
|
30
|
+
},
|
|
31
|
+
ctx,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (!sourceCurrency) {
|
|
35
|
+
throw new CurrencyNotFoundError(input.sourceCurrencyCode);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Validate target currency exists
|
|
39
|
+
const { currency: targetCurrency } = await getCurrency(
|
|
40
|
+
db,
|
|
41
|
+
{
|
|
42
|
+
code: input.targetCurrencyCode,
|
|
43
|
+
},
|
|
44
|
+
ctx,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (!targetCurrency) {
|
|
48
|
+
throw new CurrencyNotFoundError(input.targetCurrencyCode);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Validate both currencies are active
|
|
52
|
+
if (!sourceCurrency.isActive) {
|
|
53
|
+
throw new InactiveCurrencyError(input.sourceCurrencyCode);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!targetCurrency.isActive) {
|
|
57
|
+
throw new InactiveCurrencyError(input.targetCurrencyCode);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Same currency - return original amount
|
|
61
|
+
if (sourceCurrency.id === targetCurrency.id) {
|
|
62
|
+
return {
|
|
63
|
+
convertedAmount: input.amount,
|
|
64
|
+
exchangeRate: 1,
|
|
65
|
+
sourceCurrency,
|
|
66
|
+
targetCurrency,
|
|
67
|
+
exchangeRateRecord: null,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Find direct exchange rate (most recent on or before conversion date)
|
|
72
|
+
const conversionDate = new Date(input.conversionDate);
|
|
73
|
+
const directRate = await db
|
|
74
|
+
.selectFrom("ExchangeRate")
|
|
75
|
+
.selectAll()
|
|
76
|
+
.where("sourceCurrencyId", "=", sourceCurrency.id)
|
|
77
|
+
.where("targetCurrencyId", "=", targetCurrency.id)
|
|
78
|
+
.where("effectiveDate", "<=", conversionDate)
|
|
79
|
+
.orderBy("effectiveDate", "desc")
|
|
80
|
+
.executeTakeFirst();
|
|
81
|
+
|
|
82
|
+
let exchangeRate: number;
|
|
83
|
+
let exchangeRateRecord = null;
|
|
84
|
+
|
|
85
|
+
if (directRate) {
|
|
86
|
+
exchangeRate = directRate.rate;
|
|
87
|
+
exchangeRateRecord = directRate;
|
|
88
|
+
} else {
|
|
89
|
+
// Try inverse rate
|
|
90
|
+
const inverseRate = await db
|
|
91
|
+
.selectFrom("ExchangeRate")
|
|
92
|
+
.selectAll()
|
|
93
|
+
.where("sourceCurrencyId", "=", targetCurrency.id)
|
|
94
|
+
.where("targetCurrencyId", "=", sourceCurrency.id)
|
|
95
|
+
.where("effectiveDate", "<=", conversionDate)
|
|
96
|
+
.orderBy("effectiveDate", "desc")
|
|
97
|
+
.executeTakeFirst();
|
|
98
|
+
|
|
99
|
+
if (!inverseRate) {
|
|
100
|
+
throw new ExchangeRateNotFoundError(
|
|
101
|
+
`${input.sourceCurrencyCode} to ${input.targetCurrencyCode} on ${input.conversionDate}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
exchangeRate = 1 / inverseRate.rate;
|
|
106
|
+
exchangeRateRecord = inverseRate;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Calculate converted amount, rounded to target currency's decimal places
|
|
110
|
+
const rawResult = input.amount * exchangeRate;
|
|
111
|
+
const roundingFactor = Math.pow(10, targetCurrency.decimalPlaces);
|
|
112
|
+
const convertedAmount = Math.round(rawResult * roundingFactor) / roundingFactor;
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
convertedAmount,
|
|
116
|
+
exchangeRate,
|
|
117
|
+
sourceCurrency,
|
|
118
|
+
targetCurrency,
|
|
119
|
+
exchangeRateRecord,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { createMockDb } from "../../testing/index";
|
|
3
|
-
import { InsufficientPermissionError, type CommandContext } from "../../shared/internal";
|
|
4
3
|
import { DB } from "../generated/kysely-tailordb";
|
|
5
|
-
import {
|
|
4
|
+
import type { QueryContext } from "../../shared/internal";
|
|
5
|
+
import {
|
|
6
|
+
InactiveUnitError,
|
|
7
|
+
IncompatibleUnitsError,
|
|
8
|
+
UnitNotFoundError,
|
|
9
|
+
} from "../lib/errors.generated";
|
|
6
10
|
import {
|
|
7
11
|
baseUnitGram,
|
|
8
12
|
baseUnitKg,
|
|
@@ -10,13 +14,10 @@ import {
|
|
|
10
14
|
baseUnitPound,
|
|
11
15
|
inactiveUnit,
|
|
12
16
|
} from "../testing/fixtures";
|
|
13
|
-
import { convertQuantity } from "./convertQuantity";
|
|
17
|
+
import { convertQuantity } from "./convertQuantity.generated";
|
|
14
18
|
|
|
15
19
|
describe("convertQuantity", () => {
|
|
16
|
-
const ctx:
|
|
17
|
-
actorId: "test-actor",
|
|
18
|
-
permissions: ["primitives:convertQuantity"],
|
|
19
|
-
};
|
|
20
|
+
const ctx: QueryContext = { actorId: "test-actor" };
|
|
20
21
|
|
|
21
22
|
// Error cases first
|
|
22
23
|
it("throws when source unit doesn't exist", async () => {
|
|
@@ -208,12 +209,4 @@ describe("convertQuantity", () => {
|
|
|
208
209
|
|
|
209
210
|
expect(result.convertedQuantity).toBe(0);
|
|
210
211
|
});
|
|
211
|
-
|
|
212
|
-
it("throws when permission is missing", async () => {
|
|
213
|
-
const { db } = createMockDb<DB>();
|
|
214
|
-
const denied: CommandContext = { actorId: "test-actor", permissions: [] };
|
|
215
|
-
await expect(
|
|
216
|
-
convertQuantity(db, { quantity: 1, sourceUnitSymbol: "kg", targetUnitSymbol: "g" }, denied),
|
|
217
|
-
).rejects.toBeInstanceOf(InsufficientPermissionError);
|
|
218
|
-
});
|
|
219
212
|
});
|