@tailor-platform/erp-kit 0.1.2 → 0.2.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.
- package/CHANGELOG.md +12 -0
- package/README.md +80 -12
- package/dist/cli.js +1070 -450
- package/package.json +11 -8
- package/schemas/app-compose/business-flow.yml +3 -0
- package/schemas/app-compose/story.yml +1 -1
- package/schemas/module/model.yml +5 -0
- package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/SKILL.md +8 -14
- package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/SKILL.md +6 -13
- package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/SKILL.md +2 -6
- package/skills/{app-compose-6-implementation-spec → erp-kit-app-4-impl-spec}/SKILL.md +11 -22
- package/skills/erp-kit-app-5-implementation/SKILL.md +149 -0
- package/skills/erp-kit-app-5-implementation/references/backend.md +232 -0
- package/skills/erp-kit-app-5-implementation/references/frontend.md +242 -0
- 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 -35
- package/src/commands/app/index.ts +3 -3
- package/src/commands/check.test.ts +1 -1
- package/src/commands/check.ts +2 -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 +6 -4
- package/src/commands/module/list.test.ts +7 -12
- package/src/commands/module/list.ts +1 -1
- package/src/commands/scaffold-templates.ts +65 -0
- package/src/commands/scaffold.test.ts +92 -2
- package/src/commands/scaffold.ts +22 -2
- package/src/commands/sync-check.test.ts +60 -1
- package/src/commands/sync-check.ts +35 -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 +2 -2
- package/src/module.ts +44 -6
- 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/queries/ConvertAmount.md +3 -5
- package/src/modules/primitives/docs/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 +15 -4
- package/src/modules/primitives/lib/errors.generated.ts +112 -0
- package/src/modules/primitives/{permissions.ts → lib/permissions.generated.ts} +9 -8
- package/src/modules/primitives/module.ts +37 -27
- package/src/modules/primitives/query/convertAmount.generated.ts +5 -0
- package/src/modules/primitives/query/convertAmount.test.ts +2 -2
- package/src/modules/primitives/query/convertAmount.ts +27 -28
- package/src/modules/primitives/query/convertQuantity.generated.ts +5 -0
- package/src/modules/primitives/query/convertQuantity.test.ts +6 -2
- package/src/modules/primitives/query/convertQuantity.ts +49 -57
- 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/internal.ts +1 -0
- 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/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/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 +1 -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/skills/app-compose-1-requirement-analysis/references/structure.md +0 -27
- package/skills/app-compose-2-requirements-breakdown/references/screen-detailview.md +0 -106
- package/skills/app-compose-2-requirements-breakdown/references/screen-form.md +0 -139
- package/skills/app-compose-2-requirements-breakdown/references/screen-listview.md +0 -153
- package/skills/app-compose-2-requirements-breakdown/references/structure.md +0 -27
- package/skills/app-compose-3-doc-review/references/structure.md +0 -27
- package/skills/app-compose-4-design-mock/SKILL.md +0 -256
- package/skills/app-compose-4-design-mock/references/component.md +0 -50
- package/skills/app-compose-4-design-mock/references/screen-detailview.md +0 -106
- package/skills/app-compose-4-design-mock/references/screen-form.md +0 -139
- package/skills/app-compose-4-design-mock/references/screen-listview.md +0 -153
- package/skills/app-compose-4-design-mock/references/structure.md +0 -27
- package/skills/app-compose-5-design-mock-review/SKILL.md +0 -290
- package/skills/app-compose-5-design-mock-review/references/component.md +0 -50
- package/skills/app-compose-5-design-mock-review/references/screen-detailview.md +0 -106
- package/skills/app-compose-5-design-mock-review/references/screen-form.md +0 -139
- package/skills/app-compose-5-design-mock-review/references/screen-listview.md +0 -153
- package/skills/app-compose-6-implementation-spec/references/auth.md +0 -72
- package/skills/app-compose-6-implementation-spec/references/structure.md +0 -27
- package/src/modules/primitives/lib/errors.ts +0 -138
- package/src/modules/user-management/lib/errors.ts +0 -81
- /package/skills/{2-module-feature-breakdown → erp-kit-module-4-tdd}/references/models.md +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseCommandDoc, errorCodeToClassName, type ParsedCommandDoc } from "./parse-command-doc";
|
|
4
|
+
|
|
5
|
+
export function generateErrors(docs: ParsedCommandDoc[]): string {
|
|
6
|
+
const seen = new Set<string>();
|
|
7
|
+
const lines: string[] = [];
|
|
8
|
+
|
|
9
|
+
for (const doc of docs) {
|
|
10
|
+
for (const error of doc.errors) {
|
|
11
|
+
if (seen.has(error.code)) continue;
|
|
12
|
+
seen.add(error.code);
|
|
13
|
+
|
|
14
|
+
const className = errorCodeToClassName(error.code);
|
|
15
|
+
lines.push(`export const ${className} = createDomainError(`);
|
|
16
|
+
lines.push(` "${className}", "${error.code}",`);
|
|
17
|
+
const escapedDesc = error.description.replace(/`/g, "'");
|
|
18
|
+
lines.push(` (identifier: string) => \`${escapedDesc}: \${identifier}\`,`);
|
|
19
|
+
lines.push(`);`);
|
|
20
|
+
lines.push(``);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (lines.length === 0) return "";
|
|
25
|
+
|
|
26
|
+
return `// @generated — do not edit
|
|
27
|
+
import { createDomainError } from "../../shared/internal";
|
|
28
|
+
|
|
29
|
+
${lines.join("\n")}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function generateCommandStub(doc: ParsedCommandDoc): string {
|
|
33
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
34
|
+
const inputType = `${pascal}Input`;
|
|
35
|
+
|
|
36
|
+
return `import { ok, type CommandContext } from "../../shared/internal";
|
|
37
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
38
|
+
|
|
39
|
+
export interface ${inputType} {
|
|
40
|
+
// TODO: define input fields
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function run(db: DB, input: ${inputType}, ctx: CommandContext) {
|
|
44
|
+
// TODO: implement
|
|
45
|
+
return ok({});
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function generateTestStub(doc: ParsedCommandDoc): string {
|
|
51
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
52
|
+
|
|
53
|
+
return `import { describe, expect, it } from "vitest";
|
|
54
|
+
import { createMockDb } from "../../testing/index";
|
|
55
|
+
import type { CommandContext } from "../../shared/internal";
|
|
56
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
57
|
+
import { run, ${pascal}Input } from "./${doc.commandName}";
|
|
58
|
+
|
|
59
|
+
describe("${doc.commandName} - test scenario", () => {
|
|
60
|
+
const ctx: CommandContext = {
|
|
61
|
+
actorId: "test-actor",
|
|
62
|
+
permissions: ["TODO:${doc.commandName}"],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
it("should be implemented", async () => {
|
|
66
|
+
const { db } = createMockDb<DB>();
|
|
67
|
+
const result = await run(db, {} as ${pascal}Input, ctx);
|
|
68
|
+
expect(result.ok).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function generateCommandShell(doc: ParsedCommandDoc): string {
|
|
75
|
+
const lines: string[] = [
|
|
76
|
+
`// @generated — do not edit`,
|
|
77
|
+
`import { defineCommand } from "../../shared/internal";`,
|
|
78
|
+
`import { permissions } from "../lib/permissions.generated";`,
|
|
79
|
+
`import { run } from "./${doc.commandName}";`,
|
|
80
|
+
``,
|
|
81
|
+
`export const ${doc.commandName} = defineCommand(permissions.${doc.commandName}, run);`,
|
|
82
|
+
``,
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function generateQueryShell(doc: ParsedCommandDoc): string {
|
|
89
|
+
const lines: string[] = [
|
|
90
|
+
`// @generated — do not edit`,
|
|
91
|
+
`import { defineQuery } from "../../shared/internal";`,
|
|
92
|
+
`import { run } from "./${doc.commandName}";`,
|
|
93
|
+
``,
|
|
94
|
+
`export const ${doc.commandName} = defineQuery(run);`,
|
|
95
|
+
``,
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
return lines.join("\n");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function generateQueryStub(doc: ParsedCommandDoc): string {
|
|
102
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
103
|
+
const inputType = `${pascal}Input`;
|
|
104
|
+
|
|
105
|
+
return `import type { ReadonlyDB } from "../../shared/internal";
|
|
106
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
107
|
+
|
|
108
|
+
export interface ${inputType} {
|
|
109
|
+
// TODO: define input fields
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function run(db: ReadonlyDB<DB>, input: ${inputType}) {
|
|
113
|
+
// TODO: implement
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function generateQueryTestStub(doc: ParsedCommandDoc): string {
|
|
120
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
121
|
+
const inputType = `${pascal}Input`;
|
|
122
|
+
|
|
123
|
+
return `import { describe, expect, it } from "vitest";
|
|
124
|
+
import { createMockDb } from "../../testing/index";
|
|
125
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
126
|
+
import { run, type ${inputType} } from "./${doc.commandName}";
|
|
127
|
+
|
|
128
|
+
describe("${doc.commandName}", () => {
|
|
129
|
+
it("should be implemented", async () => {
|
|
130
|
+
const { db } = createMockDb<DB>();
|
|
131
|
+
const result = await run(db, {} as ${inputType});
|
|
132
|
+
expect(result).toBeDefined();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function generatePermissions(moduleName: string, commandNames: string[]): string {
|
|
139
|
+
const sorted = [...commandNames].sort();
|
|
140
|
+
const entries = sorted.map((name) => ` "${name}",`).join("\n");
|
|
141
|
+
|
|
142
|
+
return `// @generated — do not edit
|
|
143
|
+
import { definePermissions } from "../../shared/internal";
|
|
144
|
+
|
|
145
|
+
export const { permissions, own, all } = definePermissions("${moduleName}", [
|
|
146
|
+
${entries}
|
|
147
|
+
] as const);
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function runGenerateCode(modulePath: string, moduleName: string): number {
|
|
152
|
+
const docsDir = path.join(modulePath, "docs", "commands");
|
|
153
|
+
const libDir = path.join(modulePath, "lib");
|
|
154
|
+
const commandDir = path.join(modulePath, "command");
|
|
155
|
+
|
|
156
|
+
if (!fs.existsSync(docsDir)) {
|
|
157
|
+
console.error(`No docs/commands/ directory found at ${docsDir}`);
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const mdFiles = fs.readdirSync(docsDir).filter((f) => f.endsWith(".md"));
|
|
162
|
+
if (mdFiles.length === 0) {
|
|
163
|
+
console.error(`No command docs found in ${docsDir}`);
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const parsedDocs: ParsedCommandDoc[] = [];
|
|
168
|
+
|
|
169
|
+
for (const file of mdFiles) {
|
|
170
|
+
const content = fs.readFileSync(path.join(docsDir, file), "utf-8");
|
|
171
|
+
const name = path.basename(file, ".md");
|
|
172
|
+
parsedDocs.push(parseCommandDoc(name, content));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Also parse query docs for error extraction
|
|
176
|
+
const queryDocsDir = path.join(modulePath, "docs", "queries");
|
|
177
|
+
const allDocsForErrors = [...parsedDocs];
|
|
178
|
+
if (fs.existsSync(queryDocsDir)) {
|
|
179
|
+
const queryFiles = fs.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
|
|
180
|
+
for (const file of queryFiles) {
|
|
181
|
+
const content = fs.readFileSync(path.join(queryDocsDir, file), "utf-8");
|
|
182
|
+
const name = path.basename(file, ".md");
|
|
183
|
+
allDocsForErrors.push(parseCommandDoc(name, content));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let generated = 0;
|
|
188
|
+
|
|
189
|
+
const errorsContent = generateErrors(allDocsForErrors);
|
|
190
|
+
if (errorsContent) {
|
|
191
|
+
fs.mkdirSync(libDir, { recursive: true });
|
|
192
|
+
const errorsFile = path.join(libDir, "errors.generated.ts");
|
|
193
|
+
fs.writeFileSync(errorsFile, errorsContent);
|
|
194
|
+
console.log(` wrote ${path.relative(modulePath, errorsFile)}`);
|
|
195
|
+
generated++;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const commandNames = parsedDocs.map((d) => d.commandName);
|
|
199
|
+
const permissionsContent = generatePermissions(moduleName, commandNames);
|
|
200
|
+
const permissionsFile = path.join(libDir, "permissions.generated.ts");
|
|
201
|
+
fs.writeFileSync(permissionsFile, permissionsContent);
|
|
202
|
+
console.log(` wrote ${path.relative(modulePath, permissionsFile)}`);
|
|
203
|
+
generated++;
|
|
204
|
+
|
|
205
|
+
fs.mkdirSync(commandDir, { recursive: true });
|
|
206
|
+
for (const doc of parsedDocs) {
|
|
207
|
+
const implFile = path.join(commandDir, `${doc.commandName}.ts`);
|
|
208
|
+
const shellContent = generateCommandShell(doc);
|
|
209
|
+
const shellFile = path.join(commandDir, `${doc.commandName}.generated.ts`);
|
|
210
|
+
fs.writeFileSync(shellFile, shellContent);
|
|
211
|
+
console.log(` wrote ${path.relative(modulePath, shellFile)}`);
|
|
212
|
+
generated++;
|
|
213
|
+
|
|
214
|
+
if (!fs.existsSync(implFile)) {
|
|
215
|
+
fs.writeFileSync(implFile, generateCommandStub(doc));
|
|
216
|
+
console.log(` scaffolded ${path.relative(modulePath, implFile)}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const testFile = path.join(commandDir, `${doc.commandName}.test.ts`);
|
|
220
|
+
if (!fs.existsSync(testFile)) {
|
|
221
|
+
fs.writeFileSync(testFile, generateTestStub(doc));
|
|
222
|
+
console.log(` scaffolded ${path.relative(modulePath, testFile)}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Generate query files from docs/queries/*.md
|
|
227
|
+
if (fs.existsSync(queryDocsDir)) {
|
|
228
|
+
const queryFiles = fs.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
|
|
229
|
+
if (queryFiles.length > 0) {
|
|
230
|
+
const queryDir = path.join(modulePath, "query");
|
|
231
|
+
fs.mkdirSync(queryDir, { recursive: true });
|
|
232
|
+
|
|
233
|
+
for (const file of queryFiles) {
|
|
234
|
+
const content = fs.readFileSync(path.join(queryDocsDir, file), "utf-8");
|
|
235
|
+
const name = path.basename(file, ".md");
|
|
236
|
+
const doc = parseCommandDoc(name, content);
|
|
237
|
+
|
|
238
|
+
const shellFile = path.join(queryDir, `${doc.commandName}.generated.ts`);
|
|
239
|
+
fs.writeFileSync(shellFile, generateQueryShell(doc));
|
|
240
|
+
console.log(` wrote ${path.relative(modulePath, shellFile)}`);
|
|
241
|
+
generated++;
|
|
242
|
+
|
|
243
|
+
const implFile = path.join(queryDir, `${doc.commandName}.ts`);
|
|
244
|
+
if (!fs.existsSync(implFile)) {
|
|
245
|
+
fs.writeFileSync(implFile, generateQueryStub(doc));
|
|
246
|
+
console.log(` scaffolded ${path.relative(modulePath, implFile)}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const testFile = path.join(queryDir, `${doc.commandName}.test.ts`);
|
|
250
|
+
if (!fs.existsSync(testFile)) {
|
|
251
|
+
fs.writeFileSync(testFile, generateQueryTestStub(doc));
|
|
252
|
+
console.log(` scaffolded ${path.relative(modulePath, testFile)}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
console.log(`Generated ${generated} file(s) for ${moduleName}`);
|
|
259
|
+
return 0;
|
|
260
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { parseCommandDoc, errorCodeToClassName } from "./parse-command-doc";
|
|
3
|
+
|
|
4
|
+
describe("parseCommandDoc", () => {
|
|
5
|
+
it("extracts error codes and descriptions from Error Scenarios section", () => {
|
|
6
|
+
const markdown = `# CreateItem
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Creates a new item.
|
|
11
|
+
|
|
12
|
+
## Business Rules
|
|
13
|
+
|
|
14
|
+
- SKU must be unique
|
|
15
|
+
|
|
16
|
+
## Process Flow
|
|
17
|
+
|
|
18
|
+
\`\`\`mermaid
|
|
19
|
+
flowchart TD
|
|
20
|
+
A[Start] --> B[End]
|
|
21
|
+
\`\`\`
|
|
22
|
+
|
|
23
|
+
## External Dependencies
|
|
24
|
+
|
|
25
|
+
- [primitives::Unit](../../../primitives/docs/models/Unit.md) - Validates UoM
|
|
26
|
+
|
|
27
|
+
## Error Scenarios
|
|
28
|
+
|
|
29
|
+
- **DUPLICATE_SKU**: An item with the same SKU already exists
|
|
30
|
+
- **DUPLICATE_BARCODE**: An item with the same barcode already exists
|
|
31
|
+
- **UNIT_NOT_FOUND**: Referenced unit does not exist or is inactive
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const result = parseCommandDoc("CreateItem", markdown);
|
|
35
|
+
|
|
36
|
+
expect(result.commandName).toBe("createItem");
|
|
37
|
+
expect(result.errors).toEqual([
|
|
38
|
+
{ code: "DUPLICATE_SKU", description: "An item with the same SKU already exists" },
|
|
39
|
+
{ code: "DUPLICATE_BARCODE", description: "An item with the same barcode already exists" },
|
|
40
|
+
{ code: "UNIT_NOT_FOUND", description: "Referenced unit does not exist or is inactive" },
|
|
41
|
+
]);
|
|
42
|
+
expect(result.externalDependencies).toEqual([{ module: "primitives", entity: "Unit" }]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("handles command doc with no external dependencies", () => {
|
|
46
|
+
const markdown = `# CreateUser
|
|
47
|
+
|
|
48
|
+
## Overview
|
|
49
|
+
|
|
50
|
+
Creates a new user.
|
|
51
|
+
|
|
52
|
+
## Business Rules
|
|
53
|
+
|
|
54
|
+
- Email must be unique
|
|
55
|
+
|
|
56
|
+
## Process Flow
|
|
57
|
+
|
|
58
|
+
\`\`\`mermaid
|
|
59
|
+
flowchart TD
|
|
60
|
+
A[Start] --> B[End]
|
|
61
|
+
\`\`\`
|
|
62
|
+
|
|
63
|
+
## External Dependencies
|
|
64
|
+
|
|
65
|
+
- None
|
|
66
|
+
|
|
67
|
+
## Error Scenarios
|
|
68
|
+
|
|
69
|
+
- **USER_ALREADY_EXISTS**: User with this email already exists
|
|
70
|
+
- **INVALID_EMAIL**: Invalid email format
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const result = parseCommandDoc("CreateUser", markdown);
|
|
74
|
+
|
|
75
|
+
expect(result.commandName).toBe("createUser");
|
|
76
|
+
expect(result.externalDependencies).toEqual([]);
|
|
77
|
+
expect(result.errors).toHaveLength(2);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("handles command doc without External Dependencies section", () => {
|
|
81
|
+
const markdown = `# ActivateItem
|
|
82
|
+
|
|
83
|
+
## Overview
|
|
84
|
+
|
|
85
|
+
Activates a draft item.
|
|
86
|
+
|
|
87
|
+
## Business Rules
|
|
88
|
+
|
|
89
|
+
- Item must be in DRAFT status
|
|
90
|
+
|
|
91
|
+
## Process Flow
|
|
92
|
+
|
|
93
|
+
\`\`\`mermaid
|
|
94
|
+
flowchart TD
|
|
95
|
+
A[Start] --> B[End]
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
## Error Scenarios
|
|
99
|
+
|
|
100
|
+
- **ITEM_NOT_FOUND**: Item does not exist
|
|
101
|
+
- **INVALID_STATE_TRANSITION**: Item is not in a valid state for activation
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
const result = parseCommandDoc("ActivateItem", markdown);
|
|
105
|
+
|
|
106
|
+
expect(result.commandName).toBe("activateItem");
|
|
107
|
+
expect(result.externalDependencies).toEqual([]);
|
|
108
|
+
expect(result.errors).toHaveLength(2);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("extracts multiple external dependencies", () => {
|
|
112
|
+
const markdown = `# CreateOrder
|
|
113
|
+
|
|
114
|
+
## Overview
|
|
115
|
+
|
|
116
|
+
Creates an order.
|
|
117
|
+
|
|
118
|
+
## Business Rules
|
|
119
|
+
|
|
120
|
+
- Must reference valid item and customer
|
|
121
|
+
|
|
122
|
+
## Process Flow
|
|
123
|
+
|
|
124
|
+
\`\`\`mermaid
|
|
125
|
+
flowchart TD
|
|
126
|
+
A[Start] --> B[End]
|
|
127
|
+
\`\`\`
|
|
128
|
+
|
|
129
|
+
## External Dependencies
|
|
130
|
+
|
|
131
|
+
- [item-management::Item](../../../item-management/docs/models/Item.md) - Validates item
|
|
132
|
+
- [primitives::Currency](../../../primitives/docs/models/Currency.md) - Validates currency
|
|
133
|
+
|
|
134
|
+
## Error Scenarios
|
|
135
|
+
|
|
136
|
+
- **ITEM_NOT_FOUND**: Referenced item does not exist
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
const result = parseCommandDoc("CreateOrder", markdown);
|
|
140
|
+
|
|
141
|
+
expect(result.externalDependencies).toEqual([
|
|
142
|
+
{ module: "item-management", entity: "Item" },
|
|
143
|
+
{ module: "primitives", entity: "Currency" },
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("errorCodeToClassName", () => {
|
|
149
|
+
it("converts UPPER_SNAKE_CASE to PascalCase with Error suffix", () => {
|
|
150
|
+
expect(errorCodeToClassName("DUPLICATE_SKU")).toBe("DuplicateSkuError");
|
|
151
|
+
expect(errorCodeToClassName("UNIT_NOT_FOUND")).toBe("UnitNotFoundError");
|
|
152
|
+
expect(errorCodeToClassName("ITEM_NOT_FOUND")).toBe("ItemNotFoundError");
|
|
153
|
+
expect(errorCodeToClassName("USER_ALREADY_EXISTS")).toBe("UserAlreadyExistsError");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("handles single word codes", () => {
|
|
157
|
+
expect(errorCodeToClassName("UNAUTHORIZED")).toBe("UnauthorizedError");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
2
|
+
import { toString } from "mdast-util-to-string";
|
|
3
|
+
import type { Heading, List, Root, RootContent } from "mdast";
|
|
4
|
+
|
|
5
|
+
export interface ParsedError {
|
|
6
|
+
code: string;
|
|
7
|
+
description: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ParsedDependency {
|
|
11
|
+
module: string;
|
|
12
|
+
entity: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ParsedCommandDoc {
|
|
16
|
+
commandName: string;
|
|
17
|
+
errors: ParsedError[];
|
|
18
|
+
externalDependencies: ParsedDependency[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function parseCommandDoc(fileName: string, markdown: string): ParsedCommandDoc {
|
|
22
|
+
const commandName = fileName.charAt(0).toLowerCase() + fileName.slice(1);
|
|
23
|
+
const tree = fromMarkdown(markdown);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
commandName,
|
|
27
|
+
errors: parseErrorScenarios(tree),
|
|
28
|
+
externalDependencies: parseExternalDependencies(tree),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function errorCodeToClassName(code: string): string {
|
|
33
|
+
const pascal = code
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.split("_")
|
|
36
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
37
|
+
.join("");
|
|
38
|
+
return pascal + "Error";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isHeading(node: RootContent): node is Heading {
|
|
42
|
+
return node.type === "heading";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isList(node: RootContent): node is List {
|
|
46
|
+
return node.type === "list";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getNodesUnderHeading(tree: Root, headingText: string): RootContent[] {
|
|
50
|
+
const nodes: RootContent[] = [];
|
|
51
|
+
let collecting = false;
|
|
52
|
+
|
|
53
|
+
for (const node of tree.children) {
|
|
54
|
+
if (isHeading(node)) {
|
|
55
|
+
if (collecting) break;
|
|
56
|
+
if (node.depth === 2 && toString(node) === headingText) {
|
|
57
|
+
collecting = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (collecting) {
|
|
62
|
+
nodes.push(node);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return nodes;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const ERROR_PATTERN = /^([A-Z_]+):\s*(.+)$/;
|
|
70
|
+
|
|
71
|
+
function parseErrorScenarios(tree: Root): ParsedError[] {
|
|
72
|
+
const nodes = getNodesUnderHeading(tree, "Error Scenarios");
|
|
73
|
+
const errors: ParsedError[] = [];
|
|
74
|
+
|
|
75
|
+
for (const node of nodes) {
|
|
76
|
+
if (!isList(node)) continue;
|
|
77
|
+
|
|
78
|
+
for (const item of node.children) {
|
|
79
|
+
const text = toString(item);
|
|
80
|
+
const match = ERROR_PATTERN.exec(text);
|
|
81
|
+
if (match) {
|
|
82
|
+
errors.push({ code: match[1], description: match[2].trim() });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return errors;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const DEPENDENCY_PATTERN = /^([^:]+)::(.+)$/;
|
|
91
|
+
|
|
92
|
+
function parseExternalDependencies(tree: Root): ParsedDependency[] {
|
|
93
|
+
const nodes = getNodesUnderHeading(tree, "External Dependencies");
|
|
94
|
+
const deps: ParsedDependency[] = [];
|
|
95
|
+
|
|
96
|
+
for (const node of nodes) {
|
|
97
|
+
if (!isList(node)) continue;
|
|
98
|
+
|
|
99
|
+
for (const item of node.children) {
|
|
100
|
+
const firstChild = item.children[0];
|
|
101
|
+
if (firstChild?.type !== "paragraph") continue;
|
|
102
|
+
|
|
103
|
+
for (const inline of firstChild.children) {
|
|
104
|
+
if (inline.type === "link" || inline.type === "linkReference") {
|
|
105
|
+
const linkText = toString(inline);
|
|
106
|
+
const match = DEPENDENCY_PATTERN.exec(linkText);
|
|
107
|
+
if (match) {
|
|
108
|
+
deps.push({ module: match[1], entity: match[2] });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return deps;
|
|
116
|
+
}
|
package/src/integration.test.ts
CHANGED
|
@@ -11,7 +11,7 @@ const REPO_ROOT = path.resolve(__dirname, "..", "..", "..");
|
|
|
11
11
|
const skipReason = fs.existsSync(CLI_PATH) ? undefined : "Run `pnpm build` first";
|
|
12
12
|
|
|
13
13
|
describe.skipIf(skipReason)("integration", () => {
|
|
14
|
-
it("module check command runs against
|
|
14
|
+
it("module check command runs against erp-kit modules", () => {
|
|
15
15
|
try {
|
|
16
16
|
const result = execFileSync(
|
|
17
17
|
"node",
|
|
@@ -31,7 +31,7 @@ describe.skipIf(skipReason)("integration", () => {
|
|
|
31
31
|
}
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
it("module sync-check command runs against
|
|
34
|
+
it("module sync-check command runs against erp-kit modules", () => {
|
|
35
35
|
try {
|
|
36
36
|
const result = execFileSync(
|
|
37
37
|
"node",
|
package/src/module.ts
CHANGED
|
@@ -19,9 +19,9 @@ export {
|
|
|
19
19
|
permissions as primitivesPermissions,
|
|
20
20
|
own as primitivesOwn,
|
|
21
21
|
all as primitivesAll,
|
|
22
|
-
} from "./modules/primitives/permissions";
|
|
22
|
+
} from "./modules/primitives/lib/permissions.generated";
|
|
23
23
|
export {
|
|
24
|
-
|
|
24
|
+
UomCategoryNotFoundError,
|
|
25
25
|
UnitNotFoundError,
|
|
26
26
|
IncompatibleUnitsError,
|
|
27
27
|
InactiveUnitError,
|
|
@@ -36,14 +36,14 @@ export {
|
|
|
36
36
|
DuplicateCategoryNameError,
|
|
37
37
|
CategoryHasActiveUnitsError,
|
|
38
38
|
UnitNotInCategoryError,
|
|
39
|
-
|
|
39
|
+
InvalidIsoCodeError,
|
|
40
40
|
DuplicateCurrencyCodeError,
|
|
41
41
|
InvalidDecimalPlacesError,
|
|
42
42
|
CannotDeactivateBaseCurrencyError,
|
|
43
43
|
CannotSetInactiveAsBaseCurrencyError,
|
|
44
44
|
SameCurrencyPairError,
|
|
45
45
|
InvalidExchangeRateError,
|
|
46
|
-
} from "./modules/primitives/lib/errors";
|
|
46
|
+
} from "./modules/primitives/lib/errors.generated";
|
|
47
47
|
export { type ConvertQuantityInput } from "./modules/primitives/query/convertQuantity";
|
|
48
48
|
export { type ActivateCategoryInput } from "./modules/primitives/command/activateCategory";
|
|
49
49
|
export { type DeactivateCategoryInput } from "./modules/primitives/command/deactivateCategory";
|
|
@@ -60,7 +60,7 @@ export {
|
|
|
60
60
|
permissions as userManagementPermissions,
|
|
61
61
|
own as userManagementOwn,
|
|
62
62
|
all as userManagementAll,
|
|
63
|
-
} from "./modules/user-management/permissions";
|
|
63
|
+
} from "./modules/user-management/lib/permissions.generated";
|
|
64
64
|
export { AuditEventEventType, UserStatus } from "./modules/user-management/generated/enums";
|
|
65
65
|
export {
|
|
66
66
|
UserNotFoundError,
|
|
@@ -76,7 +76,7 @@ export {
|
|
|
76
76
|
DuplicateRoleNameError,
|
|
77
77
|
AssignmentNotFoundError,
|
|
78
78
|
InvalidEventTypeError,
|
|
79
|
-
} from "./modules/user-management/lib/errors";
|
|
79
|
+
} from "./modules/user-management/lib/errors.generated";
|
|
80
80
|
export { type ActivateUserInput } from "./modules/user-management/command/activateUser";
|
|
81
81
|
export { type DeactivateUserInput } from "./modules/user-management/command/deactivateUser";
|
|
82
82
|
export { type ReactivateUserInput } from "./modules/user-management/command/reactivateUser";
|
|
@@ -85,3 +85,41 @@ export { type RevokePermissionFromRoleInput } from "./modules/user-management/co
|
|
|
85
85
|
export { type AssignRoleToUserInput } from "./modules/user-management/command/assignRoleToUser";
|
|
86
86
|
export { type RevokeRoleFromUserInput } from "./modules/user-management/command/revokeRoleFromUser";
|
|
87
87
|
export { type LogAuditEventInput } from "./modules/user-management/command/logAuditEvent";
|
|
88
|
+
// item-management
|
|
89
|
+
export { defineModule as defineItemManagementModule } from "./modules/item-management/module";
|
|
90
|
+
export {
|
|
91
|
+
permissions as itemManagementPermissions,
|
|
92
|
+
own as itemManagementOwn,
|
|
93
|
+
all as itemManagementAll,
|
|
94
|
+
} from "./modules/item-management/lib/permissions.generated";
|
|
95
|
+
export { ItemStatus } from "./modules/item-management/generated/enums";
|
|
96
|
+
export {
|
|
97
|
+
ItemNotFoundError,
|
|
98
|
+
DuplicateSkuError,
|
|
99
|
+
DuplicateBarcodeError,
|
|
100
|
+
UnitNotFoundError as ItemUnitNotFoundError,
|
|
101
|
+
SkuImmutableError,
|
|
102
|
+
UomLockedError,
|
|
103
|
+
NoFieldsToUpdateError,
|
|
104
|
+
InvalidStateTransitionError,
|
|
105
|
+
DeleteNonDraftError,
|
|
106
|
+
NodeNotFoundError,
|
|
107
|
+
DuplicateNodeCodeError,
|
|
108
|
+
ParentNodeNotFoundError,
|
|
109
|
+
MaxDepthExceededError,
|
|
110
|
+
CodeImmutableError,
|
|
111
|
+
MissingRequiredFieldsError,
|
|
112
|
+
CircularReferenceError,
|
|
113
|
+
NodeHasChildrenError,
|
|
114
|
+
NodeHasAssignmentsError,
|
|
115
|
+
DuplicateAssignmentError,
|
|
116
|
+
AssignmentNotFoundError as ItemAssignmentNotFoundError,
|
|
117
|
+
} from "./modules/item-management/lib/errors.generated";
|
|
118
|
+
export { type GetItemInput } from "./modules/item-management/query/getItem";
|
|
119
|
+
export { type GetTaxonomyNodeInput } from "./modules/item-management/query/getTaxonomyNode";
|
|
120
|
+
export { type CreateItemInput } from "./modules/item-management/command/createItem";
|
|
121
|
+
export { type UpdateItemInput } from "./modules/item-management/command/updateItem";
|
|
122
|
+
export { type ActivateItemInput } from "./modules/item-management/command/activateItem";
|
|
123
|
+
export { type DeactivateItemInput } from "./modules/item-management/command/deactivateItem";
|
|
124
|
+
export { type CreateTaxonomyNodeInput } from "./modules/item-management/command/createTaxonomyNode";
|
|
125
|
+
export { type AssignItemToTaxonomyInput } from "./modules/item-management/command/assignItemToTaxonomy";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# README
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Item Management module manages items — the fundamental SKU-level entity in the ERP system. Items are the sellable, purchasable, and trackable unit that all downstream modules (Sales, Purchasing, Inventory, Manufacturing) reference as the primary transactional entity.
|
|
6
|
+
|
|
7
|
+
Each item has its own lifecycle (DRAFT → ACTIVE ↔ INACTIVE), unique SKU identity, and optional barcode. Items are classified through a flexible taxonomy system (many-to-many with TaxonomyNode) for hierarchical browsing and filtering.
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Item Lifecycle**: The canonical SKU-level entity referenced by all operational modules. Lifecycle state management (DRAFT → ACTIVE ↔ INACTIVE), SKU and barcode assignment. Items can be created directly or generated from product templates
|
|
12
|
+
- **Item Taxonomy**: Hierarchical taxonomy tree for multi-dimensional item classification (many-to-many). Items can belong to multiple taxonomy branches simultaneously
|
|
13
|
+
|
|
14
|
+
## Module Scope
|
|
15
|
+
|
|
16
|
+
### In Scope
|
|
17
|
+
|
|
18
|
+
- Item master data management (create, update, delete, status transitions)
|
|
19
|
+
- Item lifecycle state machine (DRAFT → ACTIVE ↔ INACTIVE)
|
|
20
|
+
- SKU and barcode assignment and uniqueness enforcement
|
|
21
|
+
- Unit of measure assignment per item
|
|
22
|
+
- Hierarchical item taxonomy management (TaxonomyNode tree)
|
|
23
|
+
- Item-to-taxonomy classification (many-to-many)
|
|
24
|
+
|
|
25
|
+
### Out of Scope
|
|
26
|
+
|
|
27
|
+
- Product template definitions and attribute schemas (product-management module)
|
|
28
|
+
- Variant generation from attribute combinations (product-management module)
|
|
29
|
+
- Sales price list and pricing rule management (pricing module)
|
|
30
|
+
- Inventory tracking and stock levels (inventory module)
|
|
31
|
+
- Lot and serial number management (inventory module)
|
|
32
|
+
- Vendor/supplier management (supplier-management module)
|
|
33
|
+
- Bill of Materials (manufacturing module)
|
|
34
|
+
- Product images and digital asset management
|
|
35
|
+
|
|
36
|
+
## Module Dependencies
|
|
37
|
+
|
|
38
|
+
- [primitives](../primitives/README.md) — UoM definitions for item unit of measure
|