@happyvertical/smrt-manufacturing 0.51.32 → 0.51.34

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.
@@ -0,0 +1,322 @@
1
+ /**
2
+ * ProductionService — the operational bridge from a production order to
3
+ * actual stock-mutating consume/produce events.
4
+ *
5
+ * Production orders themselves live in `@happyvertical/smrt-commerce` as a
6
+ * `Contract` STI subtype. This service does NOT modify those rows; it
7
+ * simply reads the order's `productId`, looks up the active BOM, and
8
+ * writes the right stock movements through {@link StockService}.
9
+ *
10
+ * Three methods:
11
+ *
12
+ * - {@link consumeMaterials} — for each BOM line, deduct
13
+ * `qtyPerUnit * (1 + wastePercent / 100) * runQty` from `available` at
14
+ * the production location. Stamps every movement with
15
+ * `sourceType: 'ProductionOrder'` + the production order id so audit
16
+ * queries can roll them up later. Atomic across BOM lines (one tx).
17
+ * - {@link produceFinishedGoods} — receive `runQty` of the finished
18
+ * product's SKU into `available` at the production location. The caller
19
+ * supplies the SKU id (the production order references a *product*, not
20
+ * a *SKU*; in practice the production-order-level "what we are making"
21
+ * gets resolved to a specific SKU per variant on the line items).
22
+ * - {@link runProduction} — consume materials AND receive finished
23
+ * goods in one transaction. Use this when consume + produce should be
24
+ * jointly atomic (e.g. a make-to-stock workflow where the factory step
25
+ * is invisible). Calling `consumeMaterials` then `produceFinishedGoods`
26
+ * separately is NOT jointly atomic — each opens its own transaction
27
+ * and a failure between them can leave materials deducted with no
28
+ * finished SKU receipt to balance it.
29
+ *
30
+ * ## Location convention (explicit-arg design)
31
+ *
32
+ * The original design considered carrying `originLocationId` on a per-
33
+ * package mixin of `ProductionOrder`, but that adds a meta field to a
34
+ * model owned by a sibling package. The cleaner design — and the one
35
+ * shipped here — keeps the location explicit on the call:
36
+ *
37
+ * ```typescript
38
+ * await production.consumeMaterials(order, { locationId, qty });
39
+ * await production.produceFinishedGoods(order, { locationId, qty, finishedSkuId });
40
+ * ```
41
+ *
42
+ * Callers that want a "default factory" convention can stash the
43
+ * `locationId` on their own production-order helper or wrap this service
44
+ * with a thin domain-level façade.
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+ import type { DatabaseConfig } from '@happyvertical/smrt-core';
49
+ import type { StockMovementReason, StockService } from '@happyvertical/smrt-inventory';
50
+ import { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';
51
+ import { BomLineCollection } from '../collections/BomLineCollection.js';
52
+ import type { BomLine } from '../models/BomLine.js';
53
+ /**
54
+ * Minimal shape of a production order this service needs. We deliberately
55
+ * accept this structural type rather than importing the `ProductionOrder`
56
+ * class from `@happyvertical/smrt-commerce` — that would either pull a
57
+ * dep on commerce into manufacturing or force commerce to extend a
58
+ * manufacturing interface. Plain duck typing keeps the dependency arrow
59
+ * pointing the way the architecture asks for (manufacturing builds on
60
+ * inventory; commerce stays orthogonal).
61
+ */
62
+ export interface ProductionOrderRef {
63
+ /** Production order row id. Used for `sourceId` on every movement. */
64
+ id?: string | null;
65
+ /**
66
+ * Plain string reference to the upstream product the order is asked to
67
+ * produce. Used to look up the active BOM for the consume step.
68
+ */
69
+ productId?: string;
70
+ /**
71
+ * Optional explicit BOM id. When set, {@link consumeMaterials} uses this
72
+ * row even if the product has a different `active` BOM — useful when an
73
+ * order was kicked off against a specific revision.
74
+ */
75
+ bomId?: string;
76
+ }
77
+ /**
78
+ * Per-call options for {@link ProductionService.consumeMaterials}.
79
+ */
80
+ export interface ConsumeMaterialsOptions {
81
+ /**
82
+ * Inventory location id (warehouse / factory) where the materials are
83
+ * pulled from. Required because the production order itself does not
84
+ * carry a stocking location — see "Location convention" in the module
85
+ * doc.
86
+ */
87
+ locationId: string;
88
+ /**
89
+ * Run quantity — how many finished units the BOM is being multiplied
90
+ * by. Each BOM line's `effectiveQtyPerUnit()` is multiplied by this
91
+ * number before being deducted from `available` stock.
92
+ */
93
+ qty: number;
94
+ /**
95
+ * Optional override of the reason code stamped on each
96
+ * {@link StockMovement}. Defaults to `'production_consume'`.
97
+ */
98
+ reasonCode?: StockMovementReason;
99
+ /**
100
+ * Optional free-form note attached to every emitted movement.
101
+ */
102
+ note?: string;
103
+ }
104
+ /**
105
+ * Per-call options for {@link ProductionService.produceFinishedGoods}.
106
+ */
107
+ export interface ProduceFinishedGoodsOptions {
108
+ /**
109
+ * Inventory location id (warehouse / factory) where the finished goods
110
+ * land. Required for the same reason as {@link ConsumeMaterialsOptions.locationId}.
111
+ */
112
+ locationId: string;
113
+ /**
114
+ * Quantity of finished goods produced. Always positive.
115
+ */
116
+ qty: number;
117
+ /**
118
+ * The SKU id of the finished product to receive into `available`. The
119
+ * production order references a `productId` (a `Product` from
120
+ * `@happyvertical/smrt-products`), but a product typically has multiple
121
+ * SKUs (one per variant). The caller decides which concrete SKU each
122
+ * line of the run produces.
123
+ */
124
+ finishedSkuId: string;
125
+ /**
126
+ * Optional override of the reason code stamped on the
127
+ * {@link StockMovement}. Defaults to `'production_produce'`.
128
+ */
129
+ reasonCode?: StockMovementReason;
130
+ /**
131
+ * Optional free-form note attached to the emitted movement.
132
+ */
133
+ note?: string;
134
+ }
135
+ /**
136
+ * Options accepted by {@link ProductionService.create}.
137
+ *
138
+ * Either pass a `db` for the service to construct its own
139
+ * collections + {@link StockService}, or share a pre-built `stockService`
140
+ * across subsystems.
141
+ */
142
+ export type ProductionServiceOptions = {
143
+ db: DatabaseConfig;
144
+ stockService?: StockService;
145
+ } | {
146
+ stockService: StockService;
147
+ db?: DatabaseConfig;
148
+ };
149
+ /**
150
+ * A single emitted stock movement, returned by
151
+ * {@link ProductionService.consumeMaterials} so callers can audit / log
152
+ * the impact of a production run without re-querying.
153
+ */
154
+ export interface ConsumeResult {
155
+ /** Component SKU id whose stock was deducted. */
156
+ componentSkuId: string;
157
+ /** Quantity deducted (already inflated for waste). */
158
+ qty: number;
159
+ /** Location the stock was deducted from. */
160
+ locationId: string;
161
+ }
162
+ /**
163
+ * Result of {@link ProductionService.produceFinishedGoods}.
164
+ */
165
+ export interface ProduceResult {
166
+ /** Finished SKU id whose stock increased. */
167
+ finishedSkuId: string;
168
+ /** Quantity received. */
169
+ qty: number;
170
+ /** Location the stock was received into. */
171
+ locationId: string;
172
+ }
173
+ /**
174
+ * Result of {@link ProductionService.runProduction} — the consume + produce
175
+ * pair, run inside one transaction.
176
+ */
177
+ export interface RunProductionResult {
178
+ consumed: ConsumeResult[];
179
+ produced: ProduceResult;
180
+ }
181
+ /**
182
+ * Operational bridge between a production order and the inventory ledger.
183
+ */
184
+ export declare class ProductionService {
185
+ /**
186
+ * BOM collection bound to the OUTER (non-tx) database. Used for
187
+ * pre-flight reads in `prepareConsume()` — those run before the
188
+ * stock-service transaction opens, by design (read-then-tx keeps
189
+ * the transaction window narrow and rollback only covers actual
190
+ * mutations). DO NOT add BOM writes that depend on the stock-tx
191
+ * rolling them back together — this collection is not tx-scoped
192
+ * and writes here would commit independently. If joint BOM-plus-
193
+ * stock mutations are ever needed, build tx-scoped BOM collections
194
+ * inside the `stockService.withTransaction` callback (mirror the
195
+ * pattern in StockService.withTransaction itself).
196
+ */
197
+ readonly boms: BillOfMaterialsCollection;
198
+ /**
199
+ * BomLine collection bound to the OUTER database. Same caveat as
200
+ * {@link boms} — pre-flight reads only.
201
+ */
202
+ readonly lines: BomLineCollection;
203
+ readonly stockService: StockService;
204
+ private constructor();
205
+ /** Factory — prefer {@link createProductionService}. */
206
+ static create(options: ProductionServiceOptions): Promise<ProductionService>;
207
+ /**
208
+ * Walk every BOM line for the production order's BOM and deduct
209
+ * `effectiveQtyPerUnit() * qty` from `available` at `locationId`. Each
210
+ * deduction goes through {@link StockService.adjust} with `sourceType:
211
+ * 'ProductionOrder'` and `sourceId: order.id`. Returns the list of
212
+ * movements emitted so callers can log / surface them.
213
+ *
214
+ * **Atomic across lines**: all per-line deductions and their audit
215
+ * movements run inside a single `stockService.withTransaction(...)`
216
+ * scope. An `InsufficientStockError` on line N+1 rolls back lines 1..N
217
+ * so production-order posting never leaves materials half-consumed.
218
+ * Pre-flight with {@link BomService.canProduce} when you'd rather know
219
+ * upfront than discover the shortfall mid-run.
220
+ *
221
+ * Throws {@link NoActiveBomForProductError} if no BOM id is supplied
222
+ * on the order *and* no active BOM exists for the order's `productId`.
223
+ * Throws {@link BomNotFoundError} if `order.bomId` is supplied but
224
+ * doesn't resolve. Throws plain `Error` on missing `locationId`,
225
+ * non-positive `qty`, or when both `order.bomId` and `order.productId`
226
+ * are absent (a programmer error — neither input identifies a BOM).
227
+ * Re-throws `InsufficientStockError` from `StockService.adjust` if a
228
+ * line would drive `available` below zero.
229
+ */
230
+ consumeMaterials(order: ProductionOrderRef, options: ConsumeMaterialsOptions): Promise<ConsumeResult[]>;
231
+ /**
232
+ * Receive `qty` of the finished SKU into `available` at `locationId`.
233
+ * Goes through {@link StockService.receive} with `sourceType:
234
+ * 'ProductionOrder'` and `sourceId: order.id`. Returns a single
235
+ * {@link ProduceResult} describing what landed.
236
+ *
237
+ * Throws plain `Error` on missing required arguments or non-positive
238
+ * `qty`.
239
+ */
240
+ produceFinishedGoods(order: ProductionOrderRef, options: ProduceFinishedGoodsOptions): Promise<ProduceResult>;
241
+ /**
242
+ * Atomically consume materials AND receive finished goods for one
243
+ * production run.
244
+ *
245
+ * Both calls share a single `stockService.withTransaction(...)` scope:
246
+ *
247
+ * - Materials are deducted line-by-line via {@link StockService.adjust}.
248
+ * - Finished goods are received via {@link StockService.receive}.
249
+ * - If ANY step throws — a BOM-line shortage, the produce-leg adapter
250
+ * failing, a tenancy mismatch surfaced by an interceptor — every
251
+ * write in the transaction rolls back. The materials are restored
252
+ * and the finished SKU's `available` row is unchanged. Callers
253
+ * never observe "materials deducted but finished goods missing".
254
+ *
255
+ * Use this when consume + produce should be one indivisible event —
256
+ * e.g. a make-to-stock workflow posting `production_order:completed`
257
+ * where the shop floor step is invisible to the ledger.
258
+ *
259
+ * The two-call form (call {@link consumeMaterials} then {@link
260
+ * produceFinishedGoods} separately) is still appropriate when the
261
+ * factory step is a real wall-clock gap that other observers need to
262
+ * see — work-in-progress dashboards, partial-run reporting, etc.
263
+ *
264
+ * Throws the same errors as the individual methods: missing args
265
+ * (plain `Error`), unresolvable BOM ({@link BomNotFoundError} /
266
+ * {@link NoActiveBomForProductError}), or `InsufficientStockError`
267
+ * if a line would drive `available` below zero.
268
+ */
269
+ runProduction(order: ProductionOrderRef, options: {
270
+ consume: ConsumeMaterialsOptions;
271
+ produce: ProduceFinishedGoodsOptions;
272
+ }): Promise<RunProductionResult>;
273
+ /**
274
+ * Validate consume args, resolve the BOM + lines, and pre-compute the
275
+ * mutation attribution. Anything that can throw without a tx open
276
+ * happens here so the transaction we open later doesn't have to roll
277
+ * back over a validation miss.
278
+ */
279
+ private prepareConsume;
280
+ /**
281
+ * Execute the consume leg against a caller-supplied tx-bound
282
+ * {@link StockService}. Public entry points (`consumeMaterials`,
283
+ * `runProduction`) open the tx and call through here. Each per-line
284
+ * `tx.adjust(...)` shares the same transaction so a shortfall on
285
+ * line N+1 rolls back lines 1..N.
286
+ */
287
+ private consumeMaterialsWith;
288
+ /**
289
+ * Execute the produce leg against a caller-supplied tx-bound
290
+ * {@link StockService}. Public entry points (`produceFinishedGoods`,
291
+ * `runProduction`) open the tx and call through here.
292
+ */
293
+ private produceFinishedGoodsWith;
294
+ /**
295
+ * Resolve the BOM for a production order. Order of preference:
296
+ *
297
+ * 1. Explicit `order.bomId` if supplied — the caller pinned a specific
298
+ * revision; we honor that pin or fail. We deliberately do NOT fall
299
+ * back to the active BOM when `order.bomId` is stale or mistyped,
300
+ * because silently switching recipes would have the production run
301
+ * consume materials against a different BOM than the order locked.
302
+ * 2. The active BOM for `order.productId`.
303
+ *
304
+ * Throws {@link BomNotFoundError} when an explicit `order.bomId` is
305
+ * supplied but doesn't resolve to a row.
306
+ *
307
+ * Throws {@link NoActiveBomForProductError} when no `bomId` was supplied
308
+ * and the product has no active BOM.
309
+ *
310
+ * Throws plain `Error` when neither `order.bomId` nor `order.productId`
311
+ * is supplied — there's nothing to resolve against, so we surface that
312
+ * as a programmer error with a distinct message rather than overload
313
+ * `NoActiveBomForProductError` with an empty product id.
314
+ */
315
+ private resolveBom;
316
+ }
317
+ /**
318
+ * Convenience factory. Returns a fully-initialized {@link ProductionService}.
319
+ */
320
+ export declare function createProductionService(options: ProductionServiceOptions): Promise<ProductionService>;
321
+ export type { BomLine };
322
+ //# sourceMappingURL=ProductionService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductionService.d.ts","sourceRoot":"","sources":["../../src/services/ProductionService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EACV,mBAAmB,EAEnB,YAAY,EACb,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAExE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGpD;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAChC;IAAE,EAAE,EAAE,cAAc,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CAAE,GACnD;IAAE,YAAY,EAAE,YAAY,CAAC;IAAC,EAAE,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAE1B;;;;;;;;;;;OAWG;aACa,IAAI,EAAE,yBAAyB;IAC/C;;;OAGG;aACa,KAAK,EAAE,iBAAiB;aACxB,YAAY,EAAE,YAAY;IAnB5C,OAAO;IAsBP,wDAAwD;WAC3C,MAAM,CACjB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,iBAAiB,CAAC;IAa7B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,gBAAgB,CACpB,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,aAAa,EAAE,CAAC;IAO3B;;;;;;;;OAQG;IACG,oBAAoB,CACxB,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,aAAa,CAAC;IAOzB;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,aAAa,CACjB,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE;QACP,OAAO,EAAE,uBAAuB,CAAC;QACjC,OAAO,EAAE,2BAA2B,CAAC;KACtC,GACA,OAAO,CAAC,mBAAmB,CAAC;IAc/B;;;;;OAKG;YACW,cAAc;IA4B5B;;;;;;OAMG;YACW,oBAAoB;IAuBlC;;;;OAIG;YACW,wBAAwB;IAiBtC;;;;;;;;;;;;;;;;;;;;OAoBG;YACW,UAAU;CAyBzB;AA4FD;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAE5B;AAID,YAAY,EAAE,OAAO,EAAE,CAAC"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Opt-in DispatchBus subscribers that bridge upstream production-order
3
+ * signals to {@link ProductionService}.
4
+ *
5
+ * **Off by default.** The package never auto-subscribes — consumer apps
6
+ * call {@link installManufacturingDispatchHandlers} from their `smrt.ts`
7
+ * wiring once they have decided they want a `production_order:posted`
8
+ * emit to consume materials and produce finished goods automatically.
9
+ *
10
+ * Two paired toggles let consumers wire only the legs they want:
11
+ *
12
+ * - `installProductionPosted` (default `true`) — on `production_order:posted`,
13
+ * call {@link ProductionService.consumeMaterials} once.
14
+ * - `installProductionCompleted` (default `false`) — on `production_order:completed`,
15
+ * call {@link ProductionService.produceFinishedGoods} once. Off by default
16
+ * because not every shop emits a separate "completed" event; lots of
17
+ * workflows expect consume + produce to happen atomically when the
18
+ * order is posted. Set both flags to `true` to get the split lifecycle.
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ import type { DatabaseConfig, DispatchBus } from '@happyvertical/smrt-core';
23
+ import type { StockService } from '@happyvertical/smrt-inventory';
24
+ import { type ProductionService } from './ProductionService.js';
25
+ /**
26
+ * Shape of a `production_order:posted` payload this handler expects.
27
+ *
28
+ * The producer (typically the application's smrt.ts wiring once a
29
+ * `ProductionOrder` row transitions to the posted state) packs one
30
+ * payload per posted order. The handler consumes materials for every
31
+ * unit declared by `qty`, attributed to `('ProductionOrder', productionOrderId)`
32
+ * on each emitted {@link StockMovement}.
33
+ */
34
+ export interface ProductionOrderPostedPayload {
35
+ /** Production order row id. Used for `sourceId` on every movement. */
36
+ productionOrderId: string;
37
+ /**
38
+ * Plain string reference to the upstream product the order is asked to
39
+ * produce. Used to resolve the active BOM at consume time.
40
+ */
41
+ productId: string;
42
+ /**
43
+ * Inventory location id (factory / warehouse) where materials are
44
+ * pulled from.
45
+ */
46
+ locationId: string;
47
+ /**
48
+ * Run quantity. Each BOM line's effective qty is multiplied by this
49
+ * before being deducted from `available`.
50
+ */
51
+ qty: number;
52
+ /**
53
+ * Optional explicit BOM id, e.g. when the order locked itself to a
54
+ * specific BOM revision when it was created. Falls back to the active
55
+ * BOM for `productId` if omitted.
56
+ */
57
+ bomId?: string;
58
+ /**
59
+ * Optional finished SKU id. When provided AND the
60
+ * `installProductionPosted` handler is wired to also produce in one
61
+ * shot (which only happens when `installProductionCompleted` is false
62
+ * and `producedOnPosted` is true), this is the SKU to receive into
63
+ * `available`.
64
+ */
65
+ finishedSkuId?: string;
66
+ }
67
+ /**
68
+ * Shape of a `production_order:completed` payload this handler expects.
69
+ *
70
+ * Typically emitted when the factory signs off the run and finished
71
+ * goods are physically available to ship. Only used when callers opt in
72
+ * via `installProductionCompleted`.
73
+ */
74
+ export interface ProductionOrderCompletedPayload {
75
+ /** Production order row id. Used for `sourceId` on the receipt movement. */
76
+ productionOrderId: string;
77
+ /**
78
+ * SKU id of the finished product to receive into `available`. Required.
79
+ */
80
+ finishedSkuId: string;
81
+ /** Inventory location id where the finished goods land. */
82
+ locationId: string;
83
+ /** Quantity received. */
84
+ qty: number;
85
+ }
86
+ /**
87
+ * Options accepted by {@link installManufacturingDispatchHandlers}.
88
+ *
89
+ * Provide either a pre-built {@link ProductionService} (when sharing
90
+ * across subsystems) or a `db` / `stockService` for the helper to
91
+ * construct one on first use.
92
+ */
93
+ export type InstallManufacturingDispatchHandlersOptions = {
94
+ /** Bus to subscribe on. Typically the app-wide DispatchBus. */
95
+ dispatchBus: DispatchBus;
96
+ /**
97
+ * When `true` (default), install the `production_order:posted` handler.
98
+ */
99
+ installProductionPosted?: boolean;
100
+ /**
101
+ * When `true` (default `false`), install the
102
+ * `production_order:completed` handler. Off by default because many
103
+ * workflows want consume+produce to happen on `posted`; opt in to the
104
+ * split lifecycle when your shop emits a separate completion event.
105
+ */
106
+ installProductionCompleted?: boolean;
107
+ /**
108
+ * When `true` (default `false`) AND `installProductionCompleted` is
109
+ * false, the `production_order:posted` handler also calls
110
+ * {@link ProductionService.produceFinishedGoods} after consume —
111
+ * handy for "make-to-stock instantly" workflows where the factory
112
+ * step is invisible.
113
+ *
114
+ * Ignored when `installProductionCompleted` is `true` (in that case
115
+ * production is the completed-handler's job).
116
+ */
117
+ producedOnPosted?: boolean;
118
+ } & ({
119
+ productionService: ProductionService;
120
+ db?: DatabaseConfig;
121
+ stockService?: StockService;
122
+ } | {
123
+ stockService: StockService;
124
+ db?: DatabaseConfig;
125
+ productionService?: undefined;
126
+ } | {
127
+ db: DatabaseConfig;
128
+ stockService?: undefined;
129
+ productionService?: undefined;
130
+ });
131
+ /**
132
+ * Result handle returned by {@link installManufacturingDispatchHandlers}.
133
+ * Call `dispose()` to detach the subscribers (mainly useful in tests and
134
+ * on graceful shutdown).
135
+ */
136
+ export interface InstalledManufacturingDispatchHandlers {
137
+ /** Resolved ProductionService — useful for follow-up writes in the same scope. */
138
+ productionService: ProductionService;
139
+ /** Detach every installed subscriber. Idempotent. */
140
+ dispose(): void;
141
+ }
142
+ /**
143
+ * Subscribe production-order handlers to the given DispatchBus.
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * import { createDispatchBus } from '@happyvertical/smrt-core';
148
+ * import { installManufacturingDispatchHandlers } from '@happyvertical/smrt-manufacturing';
149
+ *
150
+ * const bus = await createDispatchBus({ db });
151
+ * const handlers = await installManufacturingDispatchHandlers({
152
+ * dispatchBus: bus,
153
+ * db,
154
+ * producedOnPosted: true, // consume + produce in one shot
155
+ * });
156
+ * ```
157
+ */
158
+ export declare function installManufacturingDispatchHandlers(options: InstallManufacturingDispatchHandlersOptions): Promise<InstalledManufacturingDispatchHandlers>;
159
+ //# sourceMappingURL=dispatch-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-handlers.d.ts","sourceRoot":"","sources":["../../src/services/dispatch-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;AAIhC;;;;;;;;GAQG;AACH,MAAM,WAAW,4BAA4B;IAC3C,sEAAsE;IACtE,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,+BAA+B;IAC9C,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,+DAA+D;IAC/D,WAAW,EAAE,WAAW,CAAC;IACzB;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,CACA;IACE,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,GACD;IACE,YAAY,EAAE,YAAY,CAAC;IAC3B,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,iBAAiB,CAAC,EAAE,SAAS,CAAC;CAC/B,GACD;IACE,EAAE,EAAE,cAAc,CAAC;IACnB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,iBAAiB,CAAC,EAAE,SAAS,CAAC;CAC/B,CACJ,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,sCAAsC;IACrD,kFAAkF;IAClF,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,qDAAqD;IACrD,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oCAAoC,CACxD,OAAO,EAAE,2CAA2C,GACnD,OAAO,CAAC,sCAAsC,CAAC,CAiDjD"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Service exports for `@happyvertical/smrt-manufacturing`.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { BomService, type BomServiceOptions, type ComponentCostResolver, createBomService, } from './BomService.js';
7
+ export { type InstalledManufacturingDispatchHandlers, type InstallManufacturingDispatchHandlersOptions, installManufacturingDispatchHandlers, type ProductionOrderCompletedPayload, type ProductionOrderPostedPayload, } from './dispatch-handlers.js';
8
+ export { type ConsumeMaterialsOptions, type ConsumeResult, createProductionService, type ProduceFinishedGoodsOptions, type ProduceResult, type ProductionOrderRef, ProductionService, type ProductionServiceOptions, } from './ProductionService.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,sCAAsC,EAC3C,KAAK,2CAA2C,EAChD,oCAAoC,EACpC,KAAK,+BAA+B,EACpC,KAAK,4BAA4B,GAClC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,2BAA2B,EAChC,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,wBAAwB,CAAC"}
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-manufacturing",
6
- "packageVersion": "0.51.32",
6
+ "packageVersion": "0.51.34",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "d9dd02c19b1a6bfee5b933cce6c9f09c3ca73618fb52210c65260b1233e16991",
11
- "packageJson": "c47f3ebdb7961b5c397d36a5f945d9d52651917f7bf27b52e196df4ce1b35447",
10
+ "manifest": "fdb5cf2122ae64bdea9eec900e45aca70122fd89a87e2590b89fd2562d54d1f7",
11
+ "packageJson": "51d5869d98fc7de6a54d1a327f63f5940e424ba0f9a4154f6c254f250b12e740",
12
12
  "agents": "94d10408c859743730368f932f77e5602ebefb8fe9a8ac3178fa4b211e95ad29"
13
13
  },
14
14
  "exports": [
@@ -24,7 +24,7 @@
24
24
  "@happyvertical/sql": "catalog:",
25
25
  "@happyvertical/smrt-vitest": "workspace:*",
26
26
  "@types/node": "24.13.6",
27
- "typescript": "5.9.3",
27
+ "typescript": "6.0.3",
28
28
  "vite": "8.1.4",
29
29
  "vitest": "4.1.10"
30
30
  },