@spinekit/purchase 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/LICENSE +75 -0
  3. package/README.md +40 -0
  4. package/dist/bridges.d.mts +167 -0
  5. package/dist/bridges.mjs +151 -0
  6. package/dist/entity-model-D-03ovSg.mjs +23 -0
  7. package/dist/index.d.mts +5 -0
  8. package/dist/index.mjs +140 -0
  9. package/dist/lifecycle/purchase-lifecycle.d.mts +17 -0
  10. package/dist/lifecycle/purchase-lifecycle.mjs +120 -0
  11. package/dist/lifecycle/purchase-lifecycle.types.d.mts +32 -0
  12. package/dist/lifecycle/purchase-lifecycle.types.mjs +1 -0
  13. package/dist/payment/purchase-payment.application.d.mts +8 -0
  14. package/dist/payment/purchase-payment.application.mjs +154 -0
  15. package/dist/payment/purchase-payment.tax.d.mts +30 -0
  16. package/dist/payment/purchase-payment.tax.mjs +37 -0
  17. package/dist/payment/purchase-payment.types.d.mts +2 -0
  18. package/dist/payment/purchase-payment.types.mjs +1 -0
  19. package/dist/purchase-payment.types-hVKAeW3G.d.mts +153 -0
  20. package/dist/receipt/purchase-stock-receipt.d.mts +12 -0
  21. package/dist/receipt/purchase-stock-receipt.mjs +225 -0
  22. package/dist/receipt/purchase-stock-receipt.types.d.mts +167 -0
  23. package/dist/receipt/purchase-stock-receipt.types.mjs +1 -0
  24. package/dist/repositories/purchase-order.repository.d.mts +40 -0
  25. package/dist/repositories/purchase-order.repository.mjs +111 -0
  26. package/dist/resources/purchase-order/purchase-order.resource.d.mts +56 -0
  27. package/dist/resources/purchase-order/purchase-order.resource.mjs +193 -0
  28. package/dist/resources/supplier/supplier.model.d.mts +2 -0
  29. package/dist/resources/supplier/supplier.model.mjs +178 -0
  30. package/dist/resources/supplier/supplier.repository.d.mts +20 -0
  31. package/dist/resources/supplier/supplier.repository.mjs +62 -0
  32. package/dist/resources/supplier/supplier.resource.d.mts +34 -0
  33. package/dist/resources/supplier/supplier.resource.mjs +129 -0
  34. package/dist/resources/supplier/supplier.types.d.mts +2 -0
  35. package/dist/resources/supplier/supplier.types.mjs +1 -0
  36. package/dist/supplier.model-BcAfgyHu.d.mts +88 -0
  37. package/dist/types-T9gsXOf_.d.mts +127 -0
  38. package/package.json +129 -0
@@ -0,0 +1,88 @@
1
+ import { Connection, IndexDefinition, IndexOptions, Model, Types } from "mongoose";
2
+ import { PermissionGate } from "@spinekit/kit/permissions";
3
+ //#region src/resources/supplier/supplier.types.d.ts
4
+ interface SupplierFacet {
5
+ /** The `@classytic/party` this facet belongs to. Identity is the party's, never this doc's. */
6
+ partyId?: Types.ObjectId;
7
+ name: string;
8
+ nameNormalized?: string;
9
+ code?: string;
10
+ type: (typeof SUPPLIER_TYPES)[number];
11
+ contactPerson?: string;
12
+ phone?: string;
13
+ email?: string;
14
+ address?: string;
15
+ taxId?: string;
16
+ paymentTerms: (typeof SUPPLIER_PAYMENT_TERMS)[number];
17
+ creditDays: number;
18
+ creditLimit: number;
19
+ openingBalance: number;
20
+ isActive: boolean;
21
+ notes?: string;
22
+ tags: string[];
23
+ createdBy?: Types.ObjectId;
24
+ updatedBy?: Types.ObjectId;
25
+ createdAt?: Date;
26
+ updatedAt?: Date;
27
+ /** `null` = live, `Date` = archived. Managed by mongokit's `softDeletePlugin`. */
28
+ deletedAt?: Date | null;
29
+ }
30
+ /**
31
+ * Mongoose model NAMES this facet references.
32
+ *
33
+ * INJECTED, never literals: `'user'` is Better Auth's canonical lower-case collection in be-prod
34
+ * and a different name in another host, and a wrong `ref` yields bare ObjectIds on populate
35
+ * rather than an error.
36
+ */
37
+ interface SupplierRefs {
38
+ party: string;
39
+ user: string;
40
+ }
41
+ interface SupplierModelDeps {
42
+ connection: Connection;
43
+ refs: SupplierRefs;
44
+ /** Country-pack schema fields. See the model's docblock for why they are not declared inline. */
45
+ extraFields?: Record<string, unknown>;
46
+ /** Country-pack indexes, as `[fields, options]` pairs. */
47
+ extraIndexes?: Array<[IndexDefinition, IndexOptions]>;
48
+ /** Collection/model name. Default `'Supplier'`. */
49
+ modelName?: string;
50
+ }
51
+ /**
52
+ * Supplier code generation — a HOST port.
53
+ *
54
+ * Sequence allocation is infrastructure (be-prod runs a Mongo counter collection shared with
55
+ * every other inventory document number), and the FORMAT is a deployment's convention. Absent,
56
+ * the repository leaves `code` unset rather than inventing one: a supplier with a fabricated code
57
+ * that collides with the host's real sequence is worse than one with no code, because the unique
58
+ * index then rejects a LATER, legitimate row.
59
+ */
60
+ interface SupplierCodeGenerator {
61
+ nextCode(): Promise<string>;
62
+ }
63
+ interface SupplierRepositoryDeps {
64
+ codeGenerator?: SupplierCodeGenerator;
65
+ /** Page size defaults. Bounded because an unbounded partner list is a latent outage. */
66
+ defaultLimit?: number;
67
+ maxLimit?: number;
68
+ }
69
+ interface SupplierPermissions {
70
+ /** list + get. */
71
+ view: PermissionGate;
72
+ /** create + update + delete + the bulk import. */
73
+ manage: PermissionGate;
74
+ /**
75
+ * DELETE gate. Separate slot because a soft-delete archives a vendor the A/P ledger still
76
+ * references; a deployment may want it narrower than `manage`. Absent ⇒ `manage`.
77
+ */
78
+ delete?: PermissionGate;
79
+ }
80
+ //#endregion
81
+ //#region src/resources/supplier/supplier.model.d.ts
82
+ /** Sourcing relationship — generic enough for any jurisdiction. */
83
+ declare const SUPPLIER_TYPES: readonly ['local', 'import', 'manufacturer', 'wholesaler'];
84
+ /** Settlement basis. Terms in DAYS live on `creditDays`, so this stays a two-value axis. */
85
+ declare const SUPPLIER_PAYMENT_TERMS: readonly ['cash', 'credit'];
86
+ declare function createSupplierModel<TSupplier extends SupplierFacet = SupplierFacet>(deps: SupplierModelDeps): Model<TSupplier>;
87
+ //#endregion
88
+ export { SupplierFacet as a, SupplierRefs as c, SupplierCodeGenerator as i, SupplierRepositoryDeps as l, SUPPLIER_TYPES as n, SupplierModelDeps as o, createSupplierModel as r, SupplierPermissions as s, SUPPLIER_PAYMENT_TERMS as t };
@@ -0,0 +1,127 @@
1
+ import { PurchaseEngine } from "@classytic/purchase/engine";
2
+ import { PurchaseBridges } from "@classytic/purchase";
3
+ import { ResourceSeams } from "@classytic/arc";
4
+ import { EngineRef, EngineSlot } from "@spinekit/kit/engine-slot";
5
+ import { Connection } from "mongoose";
6
+ import { EventTransport } from "@classytic/primitives/events";
7
+ import { OutboxStore } from "@classytic/primitives/outbox";
8
+ import { PermissionGate } from "@spinekit/kit/permissions";
9
+ //#region src/types.d.ts
10
+ interface PurchasePermissions {
11
+ /** List/read purchase orders. */
12
+ view: PermissionGate;
13
+ /** Draft create/update/delete. */
14
+ manage: PermissionGate;
15
+ /** Lifecycle verbs — approve / receive. */
16
+ operate: PermissionGate;
17
+ /** cancel. */
18
+ cancel: PermissionGate;
19
+ }
20
+ interface PurchaseModuleDeps {
21
+ /**
22
+ * ── Host contribution arms ─────────────────────────────────────────────
23
+ *
24
+ * Declared because their ABSENCE made hosts spread the returned module and re-attach
25
+ * these by hand. A spread silently OVERWRITES whatever the factory contributed, and the
26
+ * host cannot see what that was — so the day this package supplies one of these arms, the
27
+ * host quietly wins and the package's version never runs.
28
+ *
29
+ * Safe in most hosts today only because the overlap does not exist YET. That is a latency,
30
+ * not a design.
31
+ */
32
+ onClose?: (fastify: unknown) => void | Promise<void>;
33
+ connection: Connection;
34
+ /**
35
+ * Functional currency of the deployment (ISO 4217) — REQUIRED by
36
+ * `createPurchaseEngine` since purchase 0.5 (a money kernel has no
37
+ * jurisdiction default; same rule as arc-invoice/arc-assets). Documents in
38
+ * another currency are rejected unless `allowForeignCurrency` is on.
39
+ */
40
+ baseCurrency: string;
41
+ /** Permit POs/bills in a currency other than `baseCurrency`. Default false. */
42
+ allowForeignCurrency?: boolean | undefined;
43
+ /** Bring-your-own engine (strangler migrations) — bridges travel with it. */
44
+ /** BYO engine. Pass a THUNK to defer allocation to arc's bootstrap phase. */
45
+ engine?: EngineRef<PurchaseEngine>;
46
+ /**
47
+ * A slot to publish the bound engine into, SHARED with the host.
48
+ *
49
+ * The seam that lets a host READ the engine without OWNING it: the host declares one slot with
50
+ * spine-kit's `createEngineSlot`, hands it here, and reads `slot.get()` wherever it needs the
51
+ * engine — this module fills it at bootstrap and empties it at teardown. Without it, a host that
52
+ * merely needs to read has to construct, which is the ownership inversion §7 fixes.
53
+ *
54
+ * Omit it and the slot stays private; nothing outside can read it.
55
+ */
56
+ slot?: EngineSlot<PurchaseEngine>;
57
+ /** Kernel ports: catalog (pricelist cost lookup), stockReceipt (WMS, idempotent), sequence. */
58
+ bridges?: PurchaseBridges;
59
+ eventTransport?: EventTransport;
60
+ /** Mongoose autoIndex — disable in production (deploy runs engine.syncIndexes())
61
+ * and in tests (await an explicit syncIndexes instead of racing teardown). */
62
+ autoIndex?: boolean;
63
+ outbox?: OutboxStore;
64
+ /** Kernel tenancy toggle. Default `false` — arc is the tenant boundary (POs
65
+ * are company-wide by convention). A vertical that scopes POs per-branch at
66
+ * the kernel flips this on (full `PurchaseTenantOptions` = BYO-engine). */
67
+ tenant?: boolean;
68
+ /**
69
+ * Waive the kernel's transaction requirement — DEV/TEST ONLY, and never defaulted here.
70
+ *
71
+ * `assertPurchaseCapabilities` requires `transactions`, and since mongokit began OBSERVING
72
+ * live topology an unreadable topology resolves `'unknown'` — which is not a yes (FAIL LOUD
73
+ * rule 3). On a replica set this flag changes nothing; on a standalone `mongod` it is the
74
+ * difference between a laptop that boots and one that refuses.
75
+ *
76
+ * OMITTED means the kernel's own fail-closed default stands, which is why this is optional
77
+ * with no default in this module: a host that forgets it gets refusal, not silent
78
+ * non-atomic money movement. Pass `!isProduction` from the host's env classifier — never
79
+ * an unconditional `true`, and never a raw `NODE_ENV` comparison (that spelling misses
80
+ * `prod`, and the miss OPENS the gate).
81
+ */
82
+ allowNonTransactional?: boolean | undefined;
83
+ /**
84
+ * Delete + re-register the kernel's models on bind. MULTI-BOOT affordance, not a mode.
85
+ *
86
+ * A suite that boots and closes the app repeatedly on ONE mongoose connection hits
87
+ * `OverwriteModelError` on the second bind, because the connection outlives the module
88
+ * scope. This permits the re-registration. The kernel refuses it under
89
+ * `NODE_ENV=production`, so it cannot become a production behaviour by accident.
90
+ *
91
+ * SHAPE, not runtime — it decides what happens to the model registry, so it travels with
92
+ * `definePurchase` rather than `bind`.
93
+ */
94
+ forceRecreate?: boolean | undefined;
95
+ /**
96
+ * Host wiring that needs the engine, resolved late (inside `resources()`, which arc
97
+ * runs after `bootstrap`). Note that a wiring function can depend on the engine
98
+ * WITHOUT taking it as a parameter — purchase's reads the singleton internally.
99
+ */
100
+ wiring?: () => PurchaseLateWiring;
101
+ /**
102
+ * Required, but may arrive via `wiring` instead when the host derives gates from the
103
+ * engine. Absent from BOTH is a hard error at mount, never a silent open door.
104
+ */
105
+ permissions?: PurchasePermissions;
106
+ /** Mount point. Default `/inventory/purchase-orders`. */
107
+ prefix?: string;
108
+ /** App-policy actions (approval preset, accounting `pay`, …). */
109
+ extraActions?: ResourceSeams['actions'];
110
+ /** App-policy routes (composite workflows, reports, exports, …). */
111
+ extraRoutes?: ResourceSeams['routes'];
112
+ /**
113
+ * Mongoose populate specs for HTTP READ paths only (list + detail) —
114
+ * display joins like `{ path: 'supplier', model: 'Supplier', select:
115
+ * 'name code' }`. Kernel verbs and internal `getById` callers stay bare.
116
+ *
117
+ * NOTE: there are deliberately NO controller/repository/schema override
118
+ * seams — the module owns HTTP, every write routes through a kernel verb,
119
+ * and the kernel's Zod schemas are the wire contract. Hosts extend via
120
+ * `extraActions`/`extraRoutes` (which ADD, never bypass).
121
+ */
122
+ readPopulate?: unknown[];
123
+ }
124
+ /** The engine-dependent subset of the deps, resolvable after bootstrap. */
125
+ type PurchaseLateWiring = Partial<Pick<PurchaseModuleDeps, 'permissions' | 'extraActions' | 'extraRoutes' | 'readPopulate'>>;
126
+ //#endregion
127
+ export { PurchasePermissions as n, PurchaseModuleDeps as t };
package/package.json ADDED
@@ -0,0 +1,129 @@
1
+ {
2
+ "name": "@spinekit/purchase",
3
+ "version": "0.1.0",
4
+ "description": "Arc module for @classytic/purchase — supplier purchase orders (draft→approve→receive with CAS + compensation + pendingStockReceipt crash heal) composed into arc apps. Paisa money wire; catalog/stockReceipt/sequence kernel ports injected; accounting posting + approval stay host seams.",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.mts",
11
+ "default": "./dist/index.mjs"
12
+ },
13
+ "./repositories/purchase-order": {
14
+ "types": "./dist/repositories/purchase-order.repository.d.mts",
15
+ "default": "./dist/repositories/purchase-order.repository.mjs"
16
+ },
17
+ "./payment": {
18
+ "types": "./dist/payment/purchase-payment.application.d.mts",
19
+ "default": "./dist/payment/purchase-payment.application.mjs"
20
+ },
21
+ "./bridges": {
22
+ "types": "./dist/bridges.d.mts",
23
+ "default": "./dist/bridges.mjs"
24
+ },
25
+ "./package.json": "./package.json",
26
+ "./receipt": {
27
+ "types": "./dist/receipt/purchase-stock-receipt.d.mts",
28
+ "default": "./dist/receipt/purchase-stock-receipt.mjs"
29
+ },
30
+ "./lifecycle": {
31
+ "types": "./dist/lifecycle/purchase-lifecycle.d.mts",
32
+ "default": "./dist/lifecycle/purchase-lifecycle.mjs"
33
+ },
34
+ "./payment/types": {
35
+ "types": "./dist/payment/purchase-payment.types.d.mts",
36
+ "default": "./dist/payment/purchase-payment.types.mjs"
37
+ },
38
+ "./payment/tax": {
39
+ "types": "./dist/payment/purchase-payment.tax.d.mts",
40
+ "default": "./dist/payment/purchase-payment.tax.mjs"
41
+ },
42
+ "./receipt/types": {
43
+ "types": "./dist/receipt/purchase-stock-receipt.types.d.mts",
44
+ "default": "./dist/receipt/purchase-stock-receipt.types.mjs"
45
+ },
46
+ "./lifecycle/types": {
47
+ "types": "./dist/lifecycle/purchase-lifecycle.types.d.mts",
48
+ "default": "./dist/lifecycle/purchase-lifecycle.types.mjs"
49
+ },
50
+ "./resources/purchase-order/resource": {
51
+ "types": "./dist/resources/purchase-order/purchase-order.resource.d.mts",
52
+ "default": "./dist/resources/purchase-order/purchase-order.resource.mjs"
53
+ },
54
+ "./resources/supplier/resource": {
55
+ "types": "./dist/resources/supplier/supplier.resource.d.mts",
56
+ "default": "./dist/resources/supplier/supplier.resource.mjs"
57
+ },
58
+ "./resources/supplier/model": {
59
+ "types": "./dist/resources/supplier/supplier.model.d.mts",
60
+ "default": "./dist/resources/supplier/supplier.model.mjs"
61
+ },
62
+ "./resources/supplier/repository": {
63
+ "types": "./dist/resources/supplier/supplier.repository.d.mts",
64
+ "default": "./dist/resources/supplier/supplier.repository.mjs"
65
+ },
66
+ "./resources/supplier/types": {
67
+ "types": "./dist/resources/supplier/supplier.types.d.mts",
68
+ "default": "./dist/resources/supplier/supplier.types.mjs"
69
+ }
70
+ },
71
+ "files": [
72
+ "LICENSE",
73
+ "dist",
74
+ "README.md",
75
+ "CHANGELOG.md"
76
+ ],
77
+ "scripts": {
78
+ "build": "tsdown",
79
+ "typecheck": "tsc --noEmit",
80
+ "test": "vitest run",
81
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
82
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
83
+ },
84
+ "peerDependencies": {
85
+ "@classytic/arc": ">=2.34.0",
86
+ "@spinekit/kit": ">=0.1.0",
87
+ "@classytic/mongokit": ">=3.34.0",
88
+ "@classytic/primitives": ">=0.23.0",
89
+ "@classytic/purchase": ">=0.6.0",
90
+ "mongoose": ">=9.4.1",
91
+ "zod": ">=4.0.0"
92
+ },
93
+ "devDependencies": {
94
+ "@classytic/arc": "^2.34.0",
95
+ "@spinekit/kit": "workspace:*",
96
+ "@classytic/arc-testkit": "^0.4.0",
97
+ "@classytic/mongokit": ">=3.34.0",
98
+ "@classytic/primitives": ">=0.23.0",
99
+ "@classytic/purchase": ">=0.6.0",
100
+ "@types/node": "^24.3.0",
101
+ "fastify": "^5.12.0",
102
+ "mongodb-memory-server": "^10.4.3",
103
+ "mongoose": "^9.7.2",
104
+ "tsdown": "^0.22.14",
105
+ "typescript": "^7.0.2",
106
+ "vitest": "^3.2.4",
107
+ "zod": "^4.3.6"
108
+ },
109
+ "author": "Classytic",
110
+ "homepage": "https://www.npmjs.com/package/@spinekit/purchase",
111
+ "repository": {
112
+ "type": "git",
113
+ "url": "git+https://github.com/classytic/spine.git",
114
+ "directory": "packages/spine-purchase"
115
+ },
116
+ "keywords": [
117
+ "arc",
118
+ "classytic",
119
+ "spine",
120
+ "erp",
121
+ "purchase",
122
+ "procurement",
123
+ "purchase-order",
124
+ "supplier"
125
+ ],
126
+ "publishConfig": {
127
+ "access": "public"
128
+ }
129
+ }