@yss123/cage-cli 0.3.0 → 0.3.2
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/README.md +7 -3
- package/dist/approval-tree.d.ts +88 -0
- package/dist/approval-tree.js +18 -0
- package/dist/approval-tree.js.map +1 -0
- package/dist/capabilities.js +46 -31
- package/dist/capabilities.js.map +1 -1
- package/dist/graph-approvals.d.ts +11 -1
- package/dist/graph-approvals.js +18 -5
- package/dist/graph-approvals.js.map +1 -1
- package/dist/graph-relations.js +10 -8
- package/dist/graph-relations.js.map +1 -1
- package/dist/graph.js +2 -0
- package/dist/graph.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/ontology.js +8 -5
- package/dist/ontology.js.map +1 -1
- package/dist/project-structure.js +2 -2
- package/dist/project-structure.js.map +1 -1
- package/dist/sync.d.ts +1 -1
- package/dist/sync.js +25 -108
- package/dist/sync.js.map +1 -1
- package/graph-ui/approval-flow.js +28 -8
- package/graph-ui/approval-parallel.js +110 -0
- package/graph-ui/approval-tree.js +17 -0
- package/graph-ui/business.css +26 -0
- package/graph-ui/business.js +30 -13
- package/graph-ui/graph-approvals.js +18 -5
- package/graph-ui/graph-relations.js +10 -8
- package/package.json +6 -6
- package/templates/app/.agents/skills/cage-development/SKILL.md +10 -0
- package/templates/app/AGENTS.md +39 -122
- package/templates/app/CLAUDE.md +6 -0
- package/templates/app/README.md +9 -8
- package/templates/app/apps/api/package.json +2 -2
- package/templates/app/apps/web/package.json +1 -1
- package/templates/app/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
- package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +6 -9
- package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +81 -49
- package/templates/app/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
- package/templates/app/apps/web/src/modules/purchase/components/approval-history.tsx +39 -4
- package/templates/app/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
- package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +9 -2
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-intelligence.css +2 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +2 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +2 -0
- package/templates/app/apps/web/src/shared/lib/error-message.ts +6 -1
- package/templates/app/apps/web/src/shared/ui/approval-member-picker.css +10 -0
- package/templates/app/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
- package/templates/app/cage.config.json +14 -0
- package/templates/app/db/migrations/0004_draft.sql +27 -0
- package/templates/app/docs/README.md +11 -0
- package/templates/app/docs/development/README.md +64 -0
- package/templates/app/docs/development/examples/note/note-detail-page.tsx +106 -0
- package/templates/app/docs/development/examples/note/note.ts +67 -0
- package/templates/app/docs/development/examples/note/notes-page.tsx +87 -0
- package/templates/{blank/docs/ai → app/docs/development/examples}/purchase-example.md +24 -10
- package/templates/{blank/docs/ai → app/docs/development/getting-started}/architecture.md +4 -4
- package/templates/app/docs/development/getting-started/first-feature.md +66 -0
- package/templates/{blank/docs/ai → app/docs/development/getting-started}/workflow.md +13 -12
- package/templates/app/docs/development/guides/approvals.md +270 -0
- package/templates/app/docs/development/guides/business-collaboration.md +63 -0
- package/templates/{blank/docs/ai → app/docs/development/guides}/files.md +5 -1
- package/templates/app/docs/{ai → development/guides}/intelligence.md +4 -4
- package/templates/{blank/docs/ai → app/docs/development/guides}/recipes.md +31 -11
- package/templates/app/docs/{ai → development/guides}/relations.md +2 -2
- package/templates/app/docs/{ai → development/guides}/routing.md +1 -1
- package/templates/{blank/docs/ai → app/docs/development/guides}/transfers.md +1 -1
- package/templates/{blank/docs/ai → app/docs/development/reference}/commenting.md +1 -1
- package/templates/app/docs/{ai → development/reference}/primitives.md +8 -62
- package/templates/app/docs/{ai → development/reference}/project-structure.md +2 -2
- package/templates/app/docs/development/skills/cage-development/SKILL.md +134 -0
- package/templates/{blank/docs/ai → app/docs/development/tools}/diagnostics.md +3 -3
- package/templates/{blank/docs/ai → app/docs/development/tools}/ontology-context.md +3 -3
- package/templates/app/docs/{ai → development/tools}/troubleshooting.md +7 -1
- package/templates/app/docs/errors/CAGE-AI-001.md +3 -3
- package/templates/app/docs/errors/CAGE-APPR-001.md +13 -10
- package/templates/app/docs/errors/CAGE-APPR-002.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-004.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-005.md +5 -5
- package/templates/app/docs/errors/CAGE-APPR-006.md +25 -10
- package/templates/app/docs/errors/CAGE-APPR-007.md +9 -7
- package/templates/app/docs/errors/CAGE-APPR-010.md +1 -1
- package/templates/app/docs/errors/CAGE-AUDIT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BIZ-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BIZ-002.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-001.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-007.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-008.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-009.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-010.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-013.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-014.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-015.md +1 -1
- package/templates/app/docs/errors/CAGE-CREATE-001.md +2 -2
- package/templates/app/docs/errors/CAGE-DECL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-002.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-003.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/app/docs/errors/CAGE-DIAG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DLQ-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-002.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-003.md +1 -1
- package/templates/app/docs/errors/CAGE-EVENT-001.md +2 -2
- package/templates/app/docs/errors/CAGE-EVENT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-001.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/app/docs/errors/CAGE-FILE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-003.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-IMPACT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-IMPACT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-005.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/app/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-003.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/app/docs/errors/CAGE-ORG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-PAIR-002.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-002.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-003.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-READS-001.md +1 -1
- package/templates/app/docs/errors/CAGE-REG-001.md +2 -2
- package/templates/app/docs/errors/CAGE-REG-002.md +1 -1
- package/templates/app/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/app/docs/errors/CAGE-REL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ROUTE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SCOPE-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SECRET-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SENS-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-005.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-010.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-011.md +1 -1
- package/templates/app/docs/errors/CAGE-STRUCT-001.md +4 -4
- package/templates/app/docs/errors/CAGE-STRUCT-002.md +3 -3
- package/templates/app/docs/errors/CAGE-SYNC-001.md +4 -4
- package/templates/app/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/app/docs/errors/CAGE-TEST-002.md +1 -1
- package/templates/app/docs/errors/CAGE-TEST-003.md +1 -1
- package/templates/app/docs/errors/CAGE-UNSAFE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-UPGRADE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-001.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-002.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-003.md +1 -1
- package/templates/app/docs/errors/README.md +3 -3
- package/templates/app/docs/errors/runtime.md +1 -1
- package/templates/app/package.json +3 -3
- package/templates/app/packages/contracts/package.json +1 -1
- package/templates/app/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
- package/templates/app/packages/contracts/src/aggregates/purchase-order/index.ts +10 -2
- package/templates/app/packages/contracts/src/index.ts +9 -0
- package/templates/app/packages/domain/package.json +5 -5
- package/templates/app/packages/domain/src/modules/purchase/approval-members.ts +54 -0
- package/templates/app/packages/domain/src/modules/purchase/approvals.ts +19 -8
- package/templates/app/packages/domain/src/modules/purchase/index.ts +2 -1
- package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +169 -7
- package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +56 -5
- package/templates/blank/.agents/skills/cage-development/SKILL.md +10 -0
- package/templates/blank/AGENTS.md +39 -122
- package/templates/blank/CLAUDE.md +6 -0
- package/templates/blank/README.md +3 -3
- package/templates/blank/apps/api/package.json +1 -1
- package/templates/blank/apps/web/package.json +1 -1
- package/templates/blank/docs/README.md +11 -0
- package/templates/blank/docs/development/README.md +64 -0
- package/templates/blank/docs/development/examples/note/note-detail-page.tsx +106 -0
- package/templates/blank/docs/development/examples/note/note.ts +67 -0
- package/templates/blank/docs/development/examples/note/notes-page.tsx +87 -0
- package/templates/{app/docs/ai → blank/docs/development/examples}/purchase-example.md +24 -10
- package/templates/{app/docs/ai → blank/docs/development/getting-started}/architecture.md +4 -4
- package/templates/blank/docs/development/getting-started/first-feature.md +66 -0
- package/templates/{app/docs/ai → blank/docs/development/getting-started}/workflow.md +13 -12
- package/templates/blank/docs/development/guides/approvals.md +270 -0
- package/templates/blank/docs/development/guides/business-collaboration.md +63 -0
- package/templates/{app/docs/ai → blank/docs/development/guides}/files.md +5 -1
- package/templates/blank/docs/{ai → development/guides}/intelligence.md +4 -4
- package/templates/{app/docs/ai → blank/docs/development/guides}/recipes.md +31 -11
- package/templates/blank/docs/{ai → development/guides}/relations.md +2 -2
- package/templates/blank/docs/{ai → development/guides}/routing.md +1 -1
- package/templates/{app/docs/ai → blank/docs/development/guides}/transfers.md +1 -1
- package/templates/{app/docs/ai → blank/docs/development/reference}/commenting.md +1 -1
- package/templates/blank/docs/{ai → development/reference}/primitives.md +8 -62
- package/templates/blank/docs/{ai → development/reference}/project-structure.md +2 -2
- package/templates/blank/docs/development/skills/cage-development/SKILL.md +134 -0
- package/templates/{app/docs/ai → blank/docs/development/tools}/diagnostics.md +3 -3
- package/templates/{app/docs/ai → blank/docs/development/tools}/ontology-context.md +3 -3
- package/templates/blank/docs/{ai → development/tools}/troubleshooting.md +7 -1
- package/templates/blank/docs/errors/CAGE-AI-001.md +3 -3
- package/templates/blank/docs/errors/CAGE-APPR-001.md +13 -10
- package/templates/blank/docs/errors/CAGE-APPR-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-005.md +5 -5
- package/templates/blank/docs/errors/CAGE-APPR-006.md +25 -10
- package/templates/blank/docs/errors/CAGE-APPR-007.md +9 -7
- package/templates/blank/docs/errors/CAGE-APPR-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-AUDIT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BIZ-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BIZ-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-007.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-008.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-009.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-013.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-014.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-015.md +1 -1
- package/templates/blank/docs/errors/CAGE-CREATE-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-DECL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-DIAG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DLQ-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-EVENT-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-EVENT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-FILE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-IMPACT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-IMPACT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-ORG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-PAIR-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-READS-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-REG-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-REG-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-REL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ROUTE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SCOPE-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SECRET-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SENS-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-011.md +1 -1
- package/templates/blank/docs/errors/CAGE-STRUCT-001.md +4 -4
- package/templates/blank/docs/errors/CAGE-STRUCT-002.md +3 -3
- package/templates/blank/docs/errors/CAGE-SYNC-001.md +4 -4
- package/templates/blank/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-TEST-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-TEST-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-003.md +1 -1
- package/templates/blank/docs/errors/README.md +3 -3
- package/templates/blank/docs/errors/runtime.md +1 -1
- package/templates/blank/docs/examples/purchase-admin/README.md +1 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +6 -9
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +81 -49
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/approval-history.tsx +39 -4
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +9 -2
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-intelligence.css +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +6 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.css +10 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
- package/templates/blank/docs/examples/purchase-admin/cage.config.json +14 -0
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +10 -2
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +9 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approval-members.ts +54 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +19 -8
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +2 -1
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +169 -7
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +56 -5
- package/templates/blank/package.json +3 -3
- package/templates/blank/packages/contracts/package.json +1 -1
- package/templates/blank/packages/domain/package.json +5 -5
- package/templates/app/docs/ai/README.md +0 -98
- package/templates/blank/docs/ai/README.md +0 -98
package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts
CHANGED
|
@@ -14,10 +14,15 @@ export function errorMessage(r: Extract<CageResult<unknown, string>, { ok: false
|
|
|
14
14
|
const labels: Record<string, string> = {
|
|
15
15
|
FORBIDDEN: '你没有执行此操作的权限',
|
|
16
16
|
INVALID_INPUT: '请检查填写内容',
|
|
17
|
-
CONFLICT: '
|
|
17
|
+
CONFLICT: '数据或任务已更新,请刷新后重试',
|
|
18
18
|
UNAUTHENTICATED: '登录已过期',
|
|
19
19
|
CONSTRAINT_VIOLATED: '未满足审批条件,请检查审批人或额度',
|
|
20
20
|
STOCK_SHORT: '库存不足',
|
|
21
|
+
REVIEWERS_REQUIRED: '请为专业会签选择 1–5 位协作人',
|
|
22
|
+
REVIEWERS_NOT_REQUIRED: '取消专业会签时请清空复核人员',
|
|
23
|
+
DUPLICATE_REVIEWERS: '专业会签人员不能重复',
|
|
24
|
+
REVIEWER_IS_APPLICANT: '申请人不能担任本单专业会签人',
|
|
25
|
+
REVIEWER_UNAVAILABLE: '所选人员已不可用,请重新核对协作名录',
|
|
21
26
|
NETWORK: '连接失败,请稍后重试',
|
|
22
27
|
};
|
|
23
28
|
return `${labels[r.domainCode ?? r.code] ?? r.domainCode ?? r.code}${r.requestId ? ` · 请求 ${r.requestId.slice(0, 8)}` : ''}`;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** 协作名录的复选行布局供采购和审批模块复用;不覆盖其他表单,窄屏单列保持可点击标签。 */
|
|
2
|
+
.approval-member-picker { border: 1px solid #dbe3d9; border-radius: 8px; padding: 14px; margin: 14px 0; min-width: 0; }
|
|
3
|
+
.approval-member-picker legend { font-size: 12px; padding: 0 6px; font-weight: 600; }
|
|
4
|
+
.approval-member-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 8px; margin: 12px 0; max-height: 250px; overflow-y: auto; }
|
|
5
|
+
.approval-member-options label { display: flex; align-items: center; gap: 10px; padding: 10px; border: 1px solid #e5e9e2; border-radius: 6px; cursor: pointer; }
|
|
6
|
+
.approval-member-options input[type='checkbox'] { width: 16px; height: 16px; flex: 0 0 auto; }
|
|
7
|
+
.approval-member-options small { display: block; margin-top: 3px; color: #647365; }
|
|
8
|
+
/* 开关与选人复选框保留原生尺寸,避免通用表单的 width:100% 让标签错位。 */
|
|
9
|
+
label.review-checkbox { display: flex; align-items: center; gap: 10px; }
|
|
10
|
+
label.review-checkbox input { width: auto; flex: none; }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 采购参与者共用的审批协作名录选择器,由采购表单和审批管理弹窗消费。
|
|
3
|
+
* 仅通过生成契约读取 ApprovalMember 安全视图,不读取完整 User 或凭账号名猜测权限。
|
|
4
|
+
* 目录是公开协作身份,不保证被选者对当前单据有审批权限;实际提交由服务端复核,空/失败不能伪装成可选名单。
|
|
5
|
+
*/
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { useQuery } from '@tanstack/react-query';
|
|
8
|
+
import { approvalMember } from '@app/contracts';
|
|
9
|
+
import { errorMessage } from '../lib/error-message';
|
|
10
|
+
import './approval-member-picker.css';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 在有名录读取权限的界面加载当前页,可按显示名/职责就地筛选;最多读取 200 项,不声称返回完整组织。
|
|
14
|
+
* page 进入查询缓存键,退出账号时由应用会话层清理;retired 默认不用于选人。
|
|
15
|
+
*/
|
|
16
|
+
export function useApprovalMembers(page = 1, includeRetired = false) {
|
|
17
|
+
return useQuery({ queryKey: ['approval-members', page, includeRetired], queryFn: async () => {
|
|
18
|
+
const result = await approvalMember.list({ page, pageSize: 200, ...(includeRetired ? {} : { status: 'active' }) });
|
|
19
|
+
if (!result.ok) throw new Error(errorMessage(result));
|
|
20
|
+
return result.data;
|
|
21
|
+
} });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 单选/多选均输出真实 userId,value 与 onChange 由父表单持有,exclude 只改善体验,不替代服务端重复票检查。
|
|
26
|
+
* max 限制本次选择数量,已选项始终可取消;翻页保留已选 ID,页面展示本次已选数量。
|
|
27
|
+
* disabled 用于在父级提交时冻结编辑;刷新与失败保留用户选择,不自动选择或发起写操作。
|
|
28
|
+
*/
|
|
29
|
+
export function ApprovalMemberPicker({ value, onChange, max = 5, exclude = [], disabled = false }: {
|
|
30
|
+
value: string[]; onChange: (ids: string[]) => void; max?: number; exclude?: string[]; disabled?: boolean;
|
|
31
|
+
}) {
|
|
32
|
+
const [search, setSearch] = useState(''), [page, setPage] = useState(1);
|
|
33
|
+
const query = useApprovalMembers(page);
|
|
34
|
+
const entries = query.data?.items.filter(member => !exclude.includes(member.userId) &&
|
|
35
|
+
`${member.displayName} ${member.specialty}`.includes(search.trim())) ?? [];
|
|
36
|
+
return <fieldset className="approval-member-picker" disabled={disabled}>
|
|
37
|
+
<legend>选择协作人员 · 已选 {value.length}/{max}</legend>
|
|
38
|
+
<input aria-label="筛选协作人员" placeholder="按姓名或职责筛选当前页" value={search} onChange={event => setSearch(event.target.value)} />
|
|
39
|
+
{query.isPending && <p role="status">正在读取协作名录…</p>}
|
|
40
|
+
{query.isError && <p role="alert">{query.error.message} <button type="button" onClick={() => void query.refetch()}>重试</button></p>}
|
|
41
|
+
<div className="approval-member-options">{entries.map(member => <label key={member.id}>
|
|
42
|
+
<input type="checkbox" checked={value.includes(member.userId)} disabled={disabled || (!value.includes(member.userId) && value.length >= max && max !== 1)}
|
|
43
|
+
onChange={event => onChange(event.target.checked ? max === 1 ? [member.userId] : [...value, member.userId] : value.filter(id => id !== member.userId))} />
|
|
44
|
+
<span><strong>{member.displayName}</strong><small>{member.specialty}</small></span>
|
|
45
|
+
</label>)}</div>
|
|
46
|
+
{query.data && entries.length === 0 && <p className="muted">当前页没有匹配人员,请调整筛选或联系名录维护人。</p>}
|
|
47
|
+
{(query.data?.total ?? 0) > 200 && <div className="actions"><button type="button" disabled={page === 1} onClick={() => setPage(page - 1)}>上一页</button>
|
|
48
|
+
<span>第 {page} 页</span><button type="button" disabled={page * 200 >= query.data!.total} onClick={() => setPage(page + 1)}>下一页</button></div>}
|
|
49
|
+
<small className="muted">名录只提供选人信息,处理权限、数据范围和最终额度仍以服务端校验为准。</small>
|
|
50
|
+
</fieldset>;
|
|
51
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"module:purchase": "采购管理",
|
|
10
10
|
"module:inventory": "库存管理",
|
|
11
11
|
"aggregate:PurchaseOrder": "采购单",
|
|
12
|
+
"aggregate:ApprovalMember": "采购审批协作名录",
|
|
12
13
|
"aggregate:Inventory": "库存台账",
|
|
13
14
|
"aggregate:InventoryLock": "库存锁",
|
|
14
15
|
"aggregate:Dept": "部门",
|
|
@@ -16,8 +17,21 @@
|
|
|
16
17
|
"aggregate:Role": "角色",
|
|
17
18
|
"command:PurchaseOrder.create": "新建采购单",
|
|
18
19
|
"command:PurchaseOrder.explainPurpose": "补充采购用途",
|
|
20
|
+
"command:PurchaseOrder.setReviewers": "设置专业复核人员",
|
|
19
21
|
"command:PurchaseOrder.returnForChanges": "退回修改",
|
|
20
22
|
"field:PurchaseOrder.reason": "采购用途",
|
|
23
|
+
"field:PurchaseOrder.needsSpecialistReview": "需要专业复核",
|
|
24
|
+
"field:PurchaseOrder.reviewerIds": "专业复核人员",
|
|
25
|
+
"command:ApprovalMember.create": "登记采购协作人",
|
|
26
|
+
"command:ApprovalMember.update": "修改协作人公开资料",
|
|
27
|
+
"command:ApprovalMember.retire": "退役采购协作人",
|
|
28
|
+
"field:ApprovalMember.userId": "协作账号",
|
|
29
|
+
"field:ApprovalMember.displayName": "公开业务姓名",
|
|
30
|
+
"field:ApprovalMember.specialty": "专业领域",
|
|
31
|
+
"state:ApprovalMember.active": "可选协作人",
|
|
32
|
+
"state:ApprovalMember.retired": "已退役协作人",
|
|
33
|
+
"capability:ApprovalMember.read": "查看采购协作名录",
|
|
34
|
+
"capability:ApprovalMember.export": "导出采购协作名录",
|
|
21
35
|
"capability:Sys.ai.use": "使用智能助理",
|
|
22
36
|
"capability:Sys.ai.models.manage": "管理 AI 模型",
|
|
23
37
|
"command:PurchaseOrder.setAttachments": "修改采购附件",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// 生成物:由 cage gen 从 domain 声明生成,不要手改。
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { client, useCageList, useCageQuery, type TransferTask, type RequestOptions } from '@yss123/cage-client';
|
|
6
|
+
|
|
7
|
+
/** 采购审批协作名录:读侧快照(敏感字段是按权限掩码后的值) */
|
|
8
|
+
export const ApprovalMemberSchema = z.object({
|
|
9
|
+
id: z.string().uuid(),
|
|
10
|
+
userId: z.string().uuid(),
|
|
11
|
+
displayName: z.string(),
|
|
12
|
+
specialty: z.string(),
|
|
13
|
+
status: z.enum(["active", "retired"]),
|
|
14
|
+
version: z.number().int(),
|
|
15
|
+
createdAt: z.string(),
|
|
16
|
+
updatedAt: z.string(),
|
|
17
|
+
});
|
|
18
|
+
export type ApprovalMember = z.infer<typeof ApprovalMemberSchema>;
|
|
19
|
+
export type ApprovalMemberStatus = ApprovalMember['status'];
|
|
20
|
+
export const ApprovalMemberStates = ['active', 'retired'] as const;
|
|
21
|
+
|
|
22
|
+
/** 命令输入:和后端命令的输入校验完全一致 */
|
|
23
|
+
export const ApprovalMemberInputs = {
|
|
24
|
+
create: z.object({ userId: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")), displayName: z.unknown(), specialty: z.unknown() }),
|
|
25
|
+
retire: z.object({ id: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")) }),
|
|
26
|
+
update: z.object({ id: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")), displayName: z.unknown(), specialty: z.unknown() }),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** ApprovalMember.create 可能返回的错误码 */
|
|
30
|
+
export type ApprovalMemberCreateError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT';
|
|
31
|
+
/** ApprovalMember.retire 可能返回的错误码 */
|
|
32
|
+
export type ApprovalMemberRetireError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
|
|
33
|
+
/** ApprovalMember.update 可能返回的错误码 */
|
|
34
|
+
export type ApprovalMemberUpdateError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
|
|
35
|
+
|
|
36
|
+
export type ApprovalMemberSortKey = 'createdAt' | 'updatedAt' | 'userId' | 'displayName' | 'specialty';
|
|
37
|
+
export interface ApprovalMemberListParams {
|
|
38
|
+
page?: number;
|
|
39
|
+
pageSize?: number;
|
|
40
|
+
/** 排序:字段名,前面加 - 表示倒序 */
|
|
41
|
+
sort?: ApprovalMemberSortKey | `-${ApprovalMemberSortKey}`;
|
|
42
|
+
/** 可以逗号分隔多个状态 */
|
|
43
|
+
status?: string;
|
|
44
|
+
createdAfter?: string;
|
|
45
|
+
createdBefore?: string;
|
|
46
|
+
userId?: string | number | boolean;
|
|
47
|
+
displayName?: string | number | boolean;
|
|
48
|
+
specialty?: string | number | boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 调用方法:每个命令一个;client 自动生成并附带幂等键和 CSRF 令牌 */
|
|
52
|
+
export const approvalMember = {
|
|
53
|
+
/** 登记采购审批协作人 */
|
|
54
|
+
create: (input: z.input<typeof ApprovalMemberInputs.create>, options: RequestOptions = {}) => client.post<ApprovalMember, ApprovalMemberCreateError>('/api/approval-member/create', input, options),
|
|
55
|
+
/** 停用采购审批协作名录成员 */
|
|
56
|
+
retire: (id: string, expectedVersion?: number, options: RequestOptions = {}) => client.post<ApprovalMember, ApprovalMemberRetireError>(`/api/approval-member/${encodeURIComponent(id)}/retire`, { expectedVersion }, options),
|
|
57
|
+
/** 修改采购协作人的公开资料 */
|
|
58
|
+
update: (id: string, input: Omit<z.input<typeof ApprovalMemberInputs.update>, 'id'>, expectedVersion?: number, options: RequestOptions = {}) => client.post<ApprovalMember, ApprovalMemberUpdateError>(`/api/approval-member/${encodeURIComponent(id)}/update`, { ...input, expectedVersion }, options),
|
|
59
|
+
/** 异步导出 CSV:返回任务,使用 client.transfer(id) 查状态,完成后下载。需要 ApprovalMember.export 能力。 */
|
|
60
|
+
export: (params: ApprovalMemberListParams = {}, options: RequestOptions = {}) => client.post<TransferTask, 'CONFLICT'>('/api/approval-member/export', params, options),
|
|
61
|
+
get: (id: string, options: RequestOptions = {}) => client.get<ApprovalMember>(`/api/approval-member/${encodeURIComponent(id)}`, undefined, options),
|
|
62
|
+
list: (params: ApprovalMemberListParams = {}, options: RequestOptions = {}) => client.get<{ items: ApprovalMember[]; total: number; page: number; pageSize: number }>('/api/approval-member', params as Record<string, unknown>, options),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** 详情查询 hook:响应也用 schema 校验;范围外返回 NOT_FOUND */
|
|
66
|
+
export const useApprovalMember = (id: string) => useCageQuery(['approval-member', id], `/api/approval-member/${encodeURIComponent(id)}`, ApprovalMemberSchema, { enabled: !!id });
|
|
67
|
+
/** 列表查询 hook:走读侧账号与安全视图 */
|
|
68
|
+
export const useApprovalMemberList = (params: ApprovalMemberListParams = {}) => useCageList(['approval-member'], '/api/approval-member', params as Record<string, unknown>, ApprovalMemberSchema);
|
|
69
|
+
/** 查询缓存键前缀:命令成功后让它失效 */
|
|
70
|
+
export const approvalMemberKeys = { all: ['approval-member'] as const, one: (id: string) => ['approval-member', id] as const };
|
|
@@ -12,6 +12,8 @@ export const PurchaseOrderSchema = z.object({
|
|
|
12
12
|
supplierPhone: z.string().nullable().optional(),
|
|
13
13
|
amount: z.string(),
|
|
14
14
|
reason: z.string().nullable().optional(),
|
|
15
|
+
needsSpecialistReview: z.boolean().nullable().optional(),
|
|
16
|
+
reviewerIds: z.array(z.string().uuid()).nullable().optional(),
|
|
15
17
|
items: z.array(z.object({ sku: z.string(), qty: z.number().int() })),
|
|
16
18
|
erpNo: z.string().nullable().optional(),
|
|
17
19
|
attachments: z.array(z.string().uuid()).max(10),
|
|
@@ -29,9 +31,10 @@ export const PurchaseOrderFileFields = {"attachments":{"maxBytes":2147483648,"ac
|
|
|
29
31
|
/** 命令输入:和后端命令的输入校验完全一致 */
|
|
30
32
|
export const PurchaseOrderInputs = {
|
|
31
33
|
cancel: z.object({ id: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")) }),
|
|
32
|
-
create: z.object({ amount: z.string(), items: z.array(z.object({ sku: z.string(), qty: z.number().int().max(9007199254740991).gt(0) })).min(1), supplierPhone: z.string().optional(), reason: z.string().max(2000).optional(), deptId: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")).optional(), attachments: z.array(z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"))).max(10).optional() }),
|
|
34
|
+
create: z.object({ amount: z.string(), items: z.array(z.object({ sku: z.string(), qty: z.number().int().max(9007199254740991).gt(0) })).min(1), supplierPhone: z.string().optional(), reason: z.string().max(2000).optional(), needsSpecialistReview: z.boolean().optional(), reviewerIds: z.array(z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"))).max(5).optional(), deptId: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")).optional(), attachments: z.array(z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"))).max(10).optional() }),
|
|
33
35
|
explainPurpose: z.object({ id: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")), reason: z.unknown() }),
|
|
34
36
|
setAttachments: z.object({ id: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")), attachments: z.array(z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"))).max(10) }),
|
|
37
|
+
setReviewers: z.object({ id: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")), needsSpecialistReview: z.boolean(), reviewerIds: z.array(z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"))).max(5) }),
|
|
35
38
|
submit: z.object({ id: z.string().uuid().regex(new RegExp("^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$")) }),
|
|
36
39
|
};
|
|
37
40
|
|
|
@@ -43,11 +46,13 @@ export type PurchaseOrderCreateError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOM
|
|
|
43
46
|
export type PurchaseOrderExplainPurposeError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'INVARIANT_VIOLATED' | 'NOT_FOUND';
|
|
44
47
|
/** PurchaseOrder.setAttachments 可能返回的错误码 */
|
|
45
48
|
export type PurchaseOrderSetAttachmentsError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'INVARIANT_VIOLATED' | 'NOT_FOUND';
|
|
49
|
+
/** PurchaseOrder.setReviewers 可能返回的错误码 */
|
|
50
|
+
export type PurchaseOrderSetReviewersError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'INVARIANT_VIOLATED' | 'NOT_FOUND';
|
|
46
51
|
/** PurchaseOrder.submit 可能返回的错误码;业务码(domainCode):NO_ITEMS */
|
|
47
52
|
export type PurchaseOrderSubmitError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'INVARIANT_VIOLATED' | 'NOT_FOUND';
|
|
48
53
|
export type PurchaseOrderSubmitDomainCode = 'NO_ITEMS';
|
|
49
54
|
|
|
50
|
-
export type PurchaseOrderSortKey = 'createdAt' | 'updatedAt' | 'deptId' | 'createdBy' | 'supplierPhone' | 'amount' | 'reason' | 'erpNo';
|
|
55
|
+
export type PurchaseOrderSortKey = 'createdAt' | 'updatedAt' | 'deptId' | 'createdBy' | 'supplierPhone' | 'amount' | 'reason' | 'needsSpecialistReview' | 'erpNo';
|
|
51
56
|
export interface PurchaseOrderListParams {
|
|
52
57
|
page?: number;
|
|
53
58
|
pageSize?: number;
|
|
@@ -61,6 +66,7 @@ export interface PurchaseOrderListParams {
|
|
|
61
66
|
createdBy?: string | number | boolean;
|
|
62
67
|
amount?: string | number | boolean;
|
|
63
68
|
reason?: string | number | boolean;
|
|
69
|
+
needsSpecialistReview?: string | number | boolean;
|
|
64
70
|
erpNo?: string | number | boolean;
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -71,6 +77,8 @@ export const purchaseOrder = {
|
|
|
71
77
|
/** 补充采购用途与必要性,保存后可以重新提交审核 */
|
|
72
78
|
explainPurpose: (id: string, input: Omit<z.input<typeof PurchaseOrderInputs.explainPurpose>, 'id'>, expectedVersion?: number, options: RequestOptions = {}) => client.post<PurchaseOrder, PurchaseOrderExplainPurposeError>(`/api/purchase-order/${encodeURIComponent(id)}/explain-purpose`, { ...input, expectedVersion }, options),
|
|
73
79
|
setAttachments: (id: string, input: Omit<z.input<typeof PurchaseOrderInputs.setAttachments>, 'id'>, expectedVersion?: number, options: RequestOptions = {}) => client.post<PurchaseOrder, PurchaseOrderSetAttachmentsError>(`/api/purchase-order/${encodeURIComponent(id)}/set-attachments`, { ...input, expectedVersion }, options),
|
|
80
|
+
/** 设置采购单专业复核需求和人员 */
|
|
81
|
+
setReviewers: (id: string, input: Omit<z.input<typeof PurchaseOrderInputs.setReviewers>, 'id'>, expectedVersion?: number, options: RequestOptions = {}) => client.post<PurchaseOrder, PurchaseOrderSetReviewersError>(`/api/purchase-order/${encodeURIComponent(id)}/set-reviewers`, { ...input, expectedVersion }, options),
|
|
74
82
|
submit: (id: string, expectedVersion?: number, options: RequestOptions = {}) => client.post<PurchaseOrder, PurchaseOrderSubmitError>(`/api/purchase-order/${encodeURIComponent(id)}/submit`, { expectedVersion }, options),
|
|
75
83
|
/** 异步导出 CSV:返回任务,使用 client.transfer(id) 查状态,完成后下载。需要 PurchaseOrder.export 能力。 */
|
|
76
84
|
export: (params: PurchaseOrderListParams = {}, options: RequestOptions = {}) => client.post<TransferTask, 'CONFLICT'>('/api/purchase-order/export', params, options),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// 生成物:由 cage gen 从 domain 声明生成,不要手改。
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
|
|
4
|
+
export * from './aggregates/approval-member';
|
|
4
5
|
export * from './aggregates/dept';
|
|
5
6
|
export * from './aggregates/inventory';
|
|
6
7
|
export * from './aggregates/inventory-lock';
|
|
@@ -9,6 +10,7 @@ export * from './aggregates/role';
|
|
|
9
10
|
export * from './aggregates/user';
|
|
10
11
|
export * from './queries';
|
|
11
12
|
import { can as baseCan, useCan as baseUseCan } from '@yss123/cage-client';
|
|
13
|
+
import { ApprovalMemberInputs } from './aggregates/approval-member';
|
|
12
14
|
import { DeptInputs } from './aggregates/dept';
|
|
13
15
|
import { InventoryInputs } from './aggregates/inventory';
|
|
14
16
|
import { InventoryLockInputs } from './aggregates/inventory-lock';
|
|
@@ -19,6 +21,11 @@ export { client, configureClient, loadMe, currentUser, CageQueryError, CageComma
|
|
|
19
21
|
|
|
20
22
|
/** 能力目录(由代码派生):写错能力点名会编译失败 */
|
|
21
23
|
export const capabilities = [
|
|
24
|
+
'ApprovalMember.create',
|
|
25
|
+
'ApprovalMember.export',
|
|
26
|
+
'ApprovalMember.read',
|
|
27
|
+
'ApprovalMember.retire',
|
|
28
|
+
'ApprovalMember.update',
|
|
22
29
|
'Dept.archive',
|
|
23
30
|
'Dept.create',
|
|
24
31
|
'Dept.export',
|
|
@@ -42,6 +49,7 @@ export const capabilities = [
|
|
|
42
49
|
'PurchaseOrder.reject',
|
|
43
50
|
'PurchaseOrder.returnForChanges',
|
|
44
51
|
'PurchaseOrder.setAttachments',
|
|
52
|
+
'PurchaseOrder.setReviewers',
|
|
45
53
|
'PurchaseOrder.submit',
|
|
46
54
|
'PurchaseOrder.unmask.phone',
|
|
47
55
|
'Role.archive',
|
|
@@ -78,6 +86,7 @@ export const useCan = (capability: Capability): boolean => baseUseCan(capability
|
|
|
78
86
|
|
|
79
87
|
/** 各聚合的命令输入 schema:按聚合名、命令名访问,可直接供表单库校验 */
|
|
80
88
|
export const schemas = {
|
|
89
|
+
ApprovalMember: ApprovalMemberInputs,
|
|
81
90
|
Dept: DeptInputs,
|
|
82
91
|
Inventory: InventoryInputs,
|
|
83
92
|
InventoryLock: InventoryLockInputs,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 采购协作名录:由业务管理员登记可参与采购复核的账号、公开业务姓名和专业领域,供草稿选人及任务操作使用。
|
|
3
|
+
* 聚合通过标准命令维护,浏览器读取 safe.approval_member;名录与 sys.user 的私密资料、角色授权分离。
|
|
4
|
+
* 名录在同租户内向具 ApprovalMember.read 且显式获 all 范围的采购参与者公开;加入名录不授予审批权限、范围或额度。
|
|
5
|
+
*/
|
|
6
|
+
import { aggregate, command, ref, scope, states, text } from '@yss123/cage-kernel';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 一位账号对应一个采购协作身份,显示姓名是管理员明确登记的业务公开值,不读取 User.name 明文。
|
|
11
|
+
* active 可供新申请选择,retired 保留旧单据的人员证据;退役不取消已经承担的审批责任或撤销真实角色。
|
|
12
|
+
*/
|
|
13
|
+
export const ApprovalMember = aggregate('ApprovalMember', {
|
|
14
|
+
description: '采购审批协作名录',
|
|
15
|
+
fields: {
|
|
16
|
+
/** 当前租户的真实用户引用;租户内唯一,禁止用多条名录为同一账号制造多个会签名额。 */
|
|
17
|
+
userId: ref('User').unique(),
|
|
18
|
+
/** 对采购协作者公开的展示姓名;与登录姓名独立维护,不包含电话、邮箱或其他个人资料。 */
|
|
19
|
+
displayName: text(80),
|
|
20
|
+
/** 供选人判断的专业领域或岗位说明,例如设备技术、合同审查;不是角色编码或权限表达式。 */
|
|
21
|
+
specialty: text(120),
|
|
22
|
+
},
|
|
23
|
+
states: states('active', 'retired', { terminal: ['retired'] }),
|
|
24
|
+
// 名录没有部门或创建人字段,因此采购参与者必须由管理员显式授予 ApprovalMember 的 all 范围。
|
|
25
|
+
scope: scope.none(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 登记采购协作人,要求 ApprovalMember.create;真实账号引用和租户约束由内核处理。
|
|
30
|
+
* 只保存公开业务名录,不改 User 的角色或密码,不代表所选用户可以批准任何采购单。
|
|
31
|
+
*/
|
|
32
|
+
export const createApprovalMember = command(ApprovalMember, {
|
|
33
|
+
name: 'create', description: '登记采购审批协作人', from: 'new', to: 'active',
|
|
34
|
+
input: z.object({ userId: z.string().uuid(), displayName: z.string().trim().min(1).max(80), specialty: z.string().trim().min(1).max(120) }),
|
|
35
|
+
handler: draft => draft,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 仅更新有效成员的展示姓名与专业说明,版本、范围和审计由内核维护。
|
|
40
|
+
* 不接受 userId,避免已被采购单引用的协作身份悄悄换人;需要另一个账号时登记新成员。
|
|
41
|
+
*/
|
|
42
|
+
export const updateApprovalMember = command(ApprovalMember, {
|
|
43
|
+
name: 'update', description: '修改采购协作人的公开资料', from: 'active', to: null,
|
|
44
|
+
input: z.object({ id: z.string().uuid(), displayName: z.string().trim().min(1).max(80), specialty: z.string().trim().min(1).max(120) }),
|
|
45
|
+
handler: (_draft, input) => ({ displayName: input.displayName, specialty: input.specialty }),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 退役成员使后续设置/提交专业复核名单拒绝此人,保留记录供历史展示。
|
|
50
|
+
* 此动作不停用登录账号、不收回角色,也不修改在途流程快照;在途处理仍按当前真实权限核验。
|
|
51
|
+
*/
|
|
52
|
+
export const retireApprovalMember = command(ApprovalMember, {
|
|
53
|
+
name: 'retire', description: '停用采购审批协作名录成员', from: 'active', to: 'retired', handler: draft => draft,
|
|
54
|
+
});
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 采购实际审批路由:AI 预审、部门审核、可选专业会签和大额财务候选受理,共用一个采购流程实例。
|
|
3
|
+
* 人工节点显式开放转办、委派和前后/并行加签;步骤/政策随提交保存,旧实例保持自己的快照。
|
|
4
|
+
* 路由只决定任务分配与签序,最终结论仍执行领域命令的职责分离、额度与库存约束。
|
|
5
|
+
*/
|
|
2
6
|
import { approval } from '@yss123/cage-kernel';
|
|
3
|
-
import { approver, gt, route, step, when } from '@yss123/cage-approval';
|
|
7
|
+
import { approver, eq, gt, route, step, when } from '@yss123/cage-approval';
|
|
4
8
|
import { PurchaseOrder, OrderSubmitted, OrderCancelled, approve, reject, returnForChanges } from './purchase-order.js';
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
|
-
* 把提交/取消事件与审批生命周期关联;routable
|
|
11
|
+
* 把提交/取消事件与审批生命周期关联;routable 仅开放金额、部门和专业复核需求/名单。
|
|
8
12
|
* 通过、驳回、退回分别调用已有 process 命令,领域状态与最终审批结论由引擎在事务内落实。
|
|
9
13
|
* automatedBy 是自动结论执行角色代码,必须由部署管理员显式配置能力和范围,声明不自动授予权限。
|
|
10
14
|
*/
|
|
@@ -12,7 +16,7 @@ export const poApproval = approval(PurchaseOrder, {
|
|
|
12
16
|
process: 'po-approval',
|
|
13
17
|
start: OrderSubmitted,
|
|
14
18
|
cancelOn: [OrderCancelled],
|
|
15
|
-
routable: ['amount', 'deptId'],
|
|
19
|
+
routable: ['amount', 'deptId', 'needsSpecialistReview', 'reviewerIds'],
|
|
16
20
|
onApproved: approve,
|
|
17
21
|
onRejected: reject,
|
|
18
22
|
onReturned: returnForChanges,
|
|
@@ -20,10 +24,12 @@ export const poApproval = approval(PurchaseOrder, {
|
|
|
20
24
|
});
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
|
-
* 顺序为 AI
|
|
27
|
+
* 顺序为 AI 材料预审、部门经理、按单据指定人员的专业会签、条件财务候选受理。
|
|
28
|
+
* needsSpecialistReview=true 才增加专业会签,名单来自提交快照且全部人必须通过;金额严格大于 50000 元才增加财务节点。
|
|
24
29
|
* AI 只读取 reason/amount/items,pass 仅推进到经理,return 回到草稿,manual 在当前节点交部门负责人。
|
|
25
30
|
* 模型失败同样转人工,最多四轮/五次工具调用/120 秒;不以失败兜底为自动批准。
|
|
26
|
-
*
|
|
31
|
+
* 财务共享一项未分配任务,finance 当前成员须先领取;所有人工动作仍需真实命令能力、范围、额度和职责分离。
|
|
32
|
+
* AI 人工接管保持原节点规则;经理、专业和财务才开放三项管理动作,新实例自动保存允许的加签位置。
|
|
27
33
|
*/
|
|
28
34
|
export const poRoute = route('po-approval', [
|
|
29
35
|
step('AI 材料预审', {
|
|
@@ -34,6 +40,11 @@ export const poRoute = route('po-approval', [
|
|
|
34
40
|
limits: { maxSteps: 4, maxToolCalls: 5, timeoutMs: 120_000 },
|
|
35
41
|
onFailure: 'manual',
|
|
36
42
|
}),
|
|
37
|
-
step('部门经理审批', approver.deptLeader('deptId')),
|
|
38
|
-
when(
|
|
43
|
+
step('部门经理审批', approver.deptLeader('deptId'), { actions: { transfer: true, delegate: true, addSign: true } }),
|
|
44
|
+
when(eq('needsSpecialistReview', true), step('专业人员会签', approver.field('reviewerIds'), {
|
|
45
|
+
mode: 'all', actions: { transfer: true, delegate: true, addSign: true },
|
|
46
|
+
})),
|
|
47
|
+
when(gt('amount', 50000), step('财务候选受理', approver.role('finance'), {
|
|
48
|
+
assignment: 'candidate', mode: 'any', actions: { transfer: true, delegate: true, addSign: true },
|
|
49
|
+
})),
|
|
39
50
|
]);
|
package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// 采购模块的公开声明;其他模块通过本入口依赖它,不穿透内部文件。
|
|
2
2
|
// index 只转导出,避免在组装入口里隐藏业务行为或产生初始化副作用。
|
|
3
|
-
//
|
|
3
|
+
// 主对象、协作名录、审批、只读统计和定时任务统一由此公开;测试文件不导出,运行时不注册测试夹具。
|
|
4
4
|
export * from './purchase-order.js';
|
|
5
|
+
export * from './approval-members.js';
|
|
5
6
|
export * from './approvals.js';
|
|
6
7
|
export * from './queries.js';
|
|
7
8
|
export * from './jobs.js';
|