@happyvertical/smrt-manufacturing 0.57.25 → 0.57.27

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["sku","trail","lines"],"sources":["../src/__smrt-register__.ts","../src/models/Operation.ts","../src/models/Assembly.ts","../src/collections/AssemblyCollection.ts","../src/models/BillOfMaterials.ts","../src/collections/BillOfMaterialsCollection.ts","../src/models/BomLine.ts","../src/collections/BomLineCollection.ts","../src/collections/OperationCollection.ts","../src/quantity.ts","../src/models/ProductionRun.ts","../src/collections/ProductionRunCollection.ts","../src/models/ProductionRunCompletion.ts","../src/collections/ProductionRunCompletionCollection.ts","../src/models/RoutingStep.ts","../src/collections/RoutingStepCollection.ts","../src/operation-types.ts","../src/production-run-types.ts","../src/tenant-scope.ts","../src/services/AssemblyService.ts","../src/services/structure-walk.ts","../src/services/BomService.ts","../src/services/ProductionService.ts","../src/services/dispatch-handlers.ts","../src/services/OperationService.ts","../src/services/ProductionRunService.ts","../src/services/RoutingService.ts"],"sourcesContent":["// Generated by @happyvertical/smrt-core smrtPlugin at library build time.\n// The package manifest is inlined so self-registration cannot miss when a\n// downstream bundler relocates this module away from dist/manifest.json\n// (https://github.com/happyvertical/smrt/issues/1506,\n// https://github.com/happyvertical/smrt/issues/1507).\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(JSON.parse(\"{\\\"version\\\":\\\"1.0.0\\\",\\\"timestamp\\\":0,\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"packageVersion\\\":\\\"0.57.25\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-manufacturing:BillOfMaterialsCollection\\\":{\\\"name\\\":\\\"billofmaterialscollection\\\",\\\"className\\\":\\\"BillOfMaterialsCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BillOfMaterialsCollection\\\",\\\"collection\\\":\\\"billofmaterialses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/BillOfMaterialsCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProduct\\\":{\\\"name\\\":\\\"findByProduct\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"productId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BillOfMaterials[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActiveForProduct\\\":{\\\"name\\\":\\\"findActiveForProduct\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"productId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BillOfMaterials | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"BomStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BillOfMaterials[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"BillOfMaterials\\\",\\\"exportName\\\":\\\"BillOfMaterialsCollection\\\",\\\"collectionExportName\\\":\\\"BillOfMaterialsCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_boms\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"product_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"version\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"effective_date\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'draft',\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"currency\\\\\\\" TEXT DEFAULT 'USD'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"product_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"effective_date\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"draft\\\"},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"currency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"USD\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_boms_product_id_version_idx\\\",\\\"columns\\\":[\\\"product_id\\\",\\\"version\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_boms_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_boms_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"b2691adc\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:BomLineCollection\\\":{\\\"name\\\":\\\"bomlinecollection\\\",\\\"className\\\":\\\"BomLineCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BomLineCollection\\\",\\\"collection\\\":\\\"bomlines\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/BomLineCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByBom\\\":{\\\"name\\\":\\\"findByBom\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"bomId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BomLine[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByComponent\\\":{\\\"name\\\":\\\"findByComponent\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"componentSkuId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BomLine[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"BomLine\\\",\\\"exportName\\\":\\\"BomLineCollection\\\",\\\"collectionExportName\\\":\\\"BomLineCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_bom_lines\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"component_sku_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"qty_per_unit\\\\\\\" REAL,\\\\n \\\\\\\"uom\\\\\\\" TEXT DEFAULT 'each',\\\\n \\\\\\\"waste_percent\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"component_sku_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"qty_per_unit\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"uom\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"each\\\"},\\\"waste_percent\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_bom_lines_bom_id_component_sku_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"component_sku_id\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_bom_lines_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_bom_lines_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"cfbae7e8\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:OperationCollection\\\":{\\\"name\\\":\\\"operationcollection\\\",\\\"className\\\":\\\"OperationCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:OperationCollection\\\",\\\"collection\\\":\\\"operations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/OperationCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByCode\\\":{\\\"name\\\":\\\"findByCode\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"code\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Operation | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"listOperations\\\":{\\\"name\\\":\\\"listOperations\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"object\\\",\\\"optional\\\":true,\\\"memberTypes\\\":[\\\"boolean\\\"]}],\\\"returnType\\\":\\\"Promise<Operation[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"Operation\\\",\\\"exportName\\\":\\\"OperationCollection\\\",\\\"collectionExportName\\\":\\\"OperationCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_operations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"code\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"name\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"is_active\\\\\\\" BOOLEAN DEFAULT TRUE,\\\\n \\\\\\\"required_qualification_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"is_active\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":true},\\\"required_qualification_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_operations_code_tenant_id_idx\\\",\\\"columns\\\":[\\\"code\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_operations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_operations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_operations_required_qualification_id_idx\\\",\\\"columns\\\":[\\\"required_qualification_id\\\"]}],\\\"version\\\":\\\"0b3134d6\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRunCollection\\\":{\\\"name\\\":\\\"productionruncollection\\\",\\\"className\\\":\\\"ProductionRunCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRunCollection\\\",\\\"collection\\\":\\\"productionruns\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/ProductionRunCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByBom\\\":{\\\"name\\\":\\\"findByBom\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"bomId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<ProductionRun[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"ProductionRunStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<ProductionRun[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"ProductionRun\\\",\\\"exportName\\\":\\\"ProductionRunCollection\\\",\\\"collectionExportName\\\":\\\"ProductionRunCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_runs\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"target_qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_qty\\\\\\\" REAL,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'planned'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"target_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"planned\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_runs_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_runs_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_runs_bom_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\"]}],\\\"version\\\":\\\"2ca8147d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletionCollection\\\":{\\\"name\\\":\\\"productionruncompletioncollection\\\",\\\"className\\\":\\\"ProductionRunCompletionCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletionCollection\\\",\\\"collection\\\":\\\"productionruncompletions\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/ProductionRunCompletionCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByRun\\\":{\\\"name\\\":\\\"findByRun\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"runId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<ProductionRunCompletion[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"ProductionRunCompletion\\\",\\\"exportName\\\":\\\"ProductionRunCompletionCollection\\\",\\\"collectionExportName\\\":\\\"ProductionRunCompletionCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_run_completions\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"run_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_at\\\\\\\" TIMESTAMP\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"run_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_production_runs\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_run_completions_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_run_completions_tenant_ea7a0d0125_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_run_completions_run_id_idx\\\",\\\"columns\\\":[\\\"run_id\\\"]}],\\\"version\\\":\\\"c940de6c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:RoutingStepCollection\\\":{\\\"name\\\":\\\"routingstepcollection\\\",\\\"className\\\":\\\"RoutingStepCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:RoutingStepCollection\\\",\\\"collection\\\":\\\"routingsteps\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/RoutingStepCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByBom\\\":{\\\"name\\\":\\\"findByBom\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"bomId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<RoutingStep[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByOperation\\\":{\\\"name\\\":\\\"findByOperation\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"operationId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<RoutingStep[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"RoutingStep\\\",\\\"exportName\\\":\\\"RoutingStepCollection\\\",\\\"collectionExportName\\\":\\\"RoutingStepCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_routing_steps\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"operation_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"sequence\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"estimated_minutes\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"operation_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_operations\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"sequence\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"estimated_minutes\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_routing_steps_bom_id_sequence_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"sequence\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_routing_steps_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_operation_id_idx\\\",\\\"columns\\\":[\\\"operation_id\\\"]}],\\\"version\\\":\\\"41ac3c66\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:Assembly\\\":{\\\"name\\\":\\\"assembly\\\",\\\"className\\\":\\\"Assembly\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:Assembly\\\",\\\"collection\\\":\\\"products\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/Assembly.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"generated\\\":true,\\\"source\\\":\\\"tenantScoped_decorator\\\",\\\"sqlType\\\":\\\"UUID\\\",\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"autoFilter\\\":true,\\\"autoPopulate\\\":true,\\\"allowSuperAdminBypass\\\":false}}},\\\"productType\\\":{\\\"type\\\":\\\"json\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The product name, as customers see it.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The product name, as customers see it.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"A few words on what the product is and who it is for.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A few words on what the product is and who it is for.\\\"}},\\\"category\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"manufacturer\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"model\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"price\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":0,\\\"description\\\":\\\"The price in minor units (cents), for example 1999 for 19.99.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The price in minor units (cents), for example 1999 for 19.99.\\\"}},\\\"defaultSalesTaxCodeId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-commerce:TaxCode\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The tax code normally used when this product is sold.\\\"}},\\\"defaultPurchaseTaxCodeId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-commerce:TaxCode\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The tax code normally used when this product is purchased.\\\"}},\\\"inStock\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":true,\\\"default\\\":true},\\\"specifications\\\":{\\\"type\\\":\\\"json\\\",\\\"required\\\":false,\\\"default\\\":{}},\\\"tags\\\":{\\\"type\\\":\\\"json\\\",\\\"required\\\":false,\\\"default\\\":[]},\\\"estimatedLabourMinutes\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"defaultOperationId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Operation\\\",\\\"_meta\\\":{\\\"nullable\\\":true,\\\"onDelete\\\":\\\"RESTRICT\\\"}},\\\"partReference\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{\\\"getSpecification\\\":{\\\"name\\\":\\\"getSpecification\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"key\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"updateSpecification\\\":{\\\"name\\\":\\\"updateSpecification\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"key\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"value\\\",\\\"type\\\":\\\"unknown\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getAssets\\\":{\\\"name\\\":\\\"getAssets\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"relationship\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"addAsset\\\":{\\\"name\\\":\\\"addAsset\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"asset\\\",\\\"type\\\":\\\"Asset\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"relationship\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":true,\\\"default\\\":\\\"attachment\\\"},{\\\"name\\\":\\\"sortOrder\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"removeAsset\\\":{\\\"name\\\":\\\"removeAsset\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"relationship\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"tableName\\\":\\\"products\\\",\\\"tableStrategy\\\":\\\"sti\\\"},\\\"extends\\\":\\\"Product\\\",\\\"extendsQualified\\\":\\\"@happyvertical/smrt-products:Product\\\",\\\"exportName\\\":\\\"Assembly\\\",\\\"collectionExportName\\\":\\\"AssemblyCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"price\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"},{\\\"field\\\":\\\"inStock\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"boolean\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"products\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"products\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"product_type\\\\\\\" TEXT,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"category\\\\\\\" TEXT,\\\\n \\\\\\\"manufacturer\\\\\\\" TEXT,\\\\n \\\\\\\"model\\\\\\\" TEXT,\\\\n \\\\\\\"price\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"default_sales_tax_code_id\\\\\\\" UUID,\\\\n \\\\\\\"default_purchase_tax_code_id\\\\\\\" UUID,\\\\n \\\\\\\"in_stock\\\\\\\" BOOLEAN DEFAULT TRUE,\\\\n \\\\\\\"specifications\\\\\\\" JSON DEFAULT '{}',\\\\n \\\\\\\"tags\\\\\\\" JSON DEFAULT '[]',\\\\n \\\\\\\"estimated_labour_minutes\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"default_operation_id\\\\\\\" UUID,\\\\n \\\\\\\"part_reference\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"product_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"manufacturer\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"model\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"price\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"default_sales_tax_code_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"default_purchase_tax_code_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"in_stock\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":true},\\\"specifications\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false,\\\"default\\\":{}},\\\"tags\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false,\\\"default\\\":[]},\\\"estimated_labour_minutes\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"default_operation_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_operations\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"part_reference\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"products_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"products_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"products_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"products_default_sales_tax_code_id_idx\\\",\\\"columns\\\":[\\\"default_sales_tax_code_id\\\"]},{\\\"name\\\":\\\"products_default_purchase_tax_code_id_idx\\\",\\\"columns\\\":[\\\"default_purchase_tax_code_id\\\"]},{\\\"name\\\":\\\"products_default_operation_id_idx\\\",\\\"columns\\\":[\\\"default_operation_id\\\"]}],\\\"version\\\":\\\"e1a54866\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:BillOfMaterials\\\":{\\\"name\\\":\\\"billofmaterials\\\",\\\"className\\\":\\\"BillOfMaterials\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BillOfMaterials\\\",\\\"collection\\\":\\\"billofmaterialses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/BillOfMaterials.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"productId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"version\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":1,\\\"_meta\\\":{\\\"required\\\":true}},\\\"effectiveDate\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"draft\\\",\\\"_meta\\\":{\\\"required\\\":true},\\\"enum\\\":[\\\"draft\\\",\\\"active\\\",\\\"superseded\\\"]},\\\"notes\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"currency\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"USD\\\"}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"conflictColumns\\\":[\\\"product_id\\\",\\\"version\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":true,\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"BillOfMaterials\\\",\\\"collectionExportName\\\":\\\"BillOfMaterialsCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"productId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"version\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"},{\\\"field\\\":\\\"status\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_boms\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"product_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"version\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"effective_date\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'draft',\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"currency\\\\\\\" TEXT DEFAULT 'USD'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"product_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"effective_date\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"draft\\\"},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"currency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"USD\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_boms_product_id_version_idx\\\",\\\"columns\\\":[\\\"product_id\\\",\\\"version\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_boms_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_boms_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"b2691adc\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:BomLine\\\":{\\\"name\\\":\\\"bomline\\\",\\\"className\\\":\\\"BomLine\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BomLine\\\",\\\"collection\\\":\\\"bomlines\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/BomLine.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"bomId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"componentSkuId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"qtyPerUnit\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"uom\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"each\\\"},\\\"wastePercent\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"notes\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{\\\"effectiveQtyPerUnit\\\":{\\\"name\\\":\\\"effectiveQtyPerUnit\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"number\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"conflictColumns\\\":[\\\"bom_id\\\",\\\"component_sku_id\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"BomLine\\\",\\\"collectionExportName\\\":\\\"BomLineCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"bomId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"componentSkuId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_bom_lines\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"component_sku_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"qty_per_unit\\\\\\\" REAL,\\\\n \\\\\\\"uom\\\\\\\" TEXT DEFAULT 'each',\\\\n \\\\\\\"waste_percent\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"component_sku_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"qty_per_unit\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"uom\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"each\\\"},\\\"waste_percent\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_bom_lines_bom_id_component_sku_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"component_sku_id\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_bom_lines_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_bom_lines_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"cfbae7e8\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:Operation\\\":{\\\"name\\\":\\\"operation\\\",\\\"className\\\":\\\"Operation\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:Operation\\\",\\\"collection\\\":\\\"operations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/Operation.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"code\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"category\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"isActive\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":false,\\\"default\\\":true},\\\"requiredQualificationId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-human-resources:Qualification\\\",\\\"_meta\\\":{\\\"nullable\\\":true}}},\\\"methods\\\":{\\\"delete\\\":{\\\"name\\\":\\\"delete\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"conflictColumns\\\":[\\\"code\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"Operation\\\",\\\"collectionExportName\\\":\\\"OperationCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"code\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"name\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_operations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"code\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"name\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"is_active\\\\\\\" BOOLEAN DEFAULT TRUE,\\\\n \\\\\\\"required_qualification_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"is_active\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":true},\\\"required_qualification_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_operations_code_tenant_id_idx\\\",\\\"columns\\\":[\\\"code\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_operations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_operations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_operations_required_qualification_id_idx\\\",\\\"columns\\\":[\\\"required_qualification_id\\\"]}],\\\"version\\\":\\\"0b3134d6\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRun\\\":{\\\"name\\\":\\\"productionrun\\\",\\\"className\\\":\\\"ProductionRun\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRun\\\",\\\"collection\\\":\\\"productionruns\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/ProductionRun.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"bomId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"BillOfMaterials\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"RESTRICT\\\"}},\\\"targetQty\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"completedQty\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"planned\\\",\\\"_meta\\\":{\\\"required\\\":true},\\\"enum\\\":[\\\"planned\\\",\\\"in_progress\\\",\\\"done\\\",\\\"cancelled\\\"]}},\\\"methods\\\":{\\\"remainingQty\\\":{\\\"name\\\":\\\"remainingQty\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"number\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"ProductionRun\\\",\\\"collectionExportName\\\":\\\"ProductionRunCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"bomId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"status\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_runs\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"target_qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_qty\\\\\\\" REAL,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'planned'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"target_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"planned\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_runs_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_runs_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_runs_bom_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\"]}],\\\"version\\\":\\\"2ca8147d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletion\\\":{\\\"name\\\":\\\"productionruncompletion\\\",\\\"className\\\":\\\"ProductionRunCompletion\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletion\\\",\\\"collection\\\":\\\"productionruncompletions\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/ProductionRunCompletion.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"runId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"ProductionRun\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"CASCADE\\\"}},\\\"qty\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"completedAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"ProductionRunCompletion\\\",\\\"collectionExportName\\\":\\\"ProductionRunCompletionCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"runId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_run_completions\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"run_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_at\\\\\\\" TIMESTAMP\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"run_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_production_runs\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_run_completions_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_run_completions_tenant_ea7a0d0125_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_run_completions_run_id_idx\\\",\\\"columns\\\":[\\\"run_id\\\"]}],\\\"version\\\":\\\"c940de6c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:RoutingStep\\\":{\\\"name\\\":\\\"routingstep\\\",\\\"className\\\":\\\"RoutingStep\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:RoutingStep\\\",\\\"collection\\\":\\\"routingsteps\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/RoutingStep.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"bomId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"BillOfMaterials\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"CASCADE\\\"}},\\\"operationId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"Operation\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"RESTRICT\\\"}},\\\"sequence\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":1,\\\"_meta\\\":{\\\"required\\\":true}},\\\"estimatedMinutes\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"notes\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"conflictColumns\\\":[\\\"bom_id\\\",\\\"sequence\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"RoutingStep\\\",\\\"collectionExportName\\\":\\\"RoutingStepCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"bomId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"operationId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"sequence\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_routing_steps\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"operation_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"sequence\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"estimated_minutes\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"operation_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_operations\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"sequence\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"estimated_minutes\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_routing_steps_bom_id_sequence_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"sequence\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_routing_steps_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_operation_id_idx\\\",\\\"columns\\\":[\\\"operation_id\\\"]}],\\\"version\\\":\\\"41ac3c66\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\",\\\"@happyvertical/smrt-inventory\\\",\\\"@happyvertical/smrt-products\\\",\\\"@happyvertical/smrt-tenancy\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3616\\\",\\\"#3624\\\",\\\"#3626\\\"],\\\"via\\\":[\\\"@happyvertical/smrt-products\\\",\\\"@happyvertical/smrt-tenancy\\\"]},\\\"stampsConstructors\\\":true}\"));\n","/**\n * Operation — one kind of work done to build something: cut, weld, assemble,\n * inspect, bake, pack. A managed list per tenant that routing steps point at.\n *\n * Retiring an operation (`isActive = false`) keeps it on every routing and\n * history that already names it and takes it out of pickers; an operation is\n * never deleted. Time recorded against an operation is not modelled here — it\n * stays in `@happyvertical/smrt-timesheets` or the application.\n *\n * Strictly industry-neutral.\n *\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Options accepted by the {@link Operation} constructor.\n */\nexport interface OperationOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n code?: string;\n name?: string;\n category?: string;\n isActive?: boolean;\n requiredQualificationId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_operations',\n // Natural key: one operation per code per tenant.\n conflictColumns: ['code', 'tenant_id'],\n // Writes go through OperationService, which rejects a duplicate code and\n // keeps the code fixed; generated create/update would upsert or rename.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class Operation extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the operation is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Short stable code, unique per tenant (`CUT`, `WELD`). Treated as the\n * operation's identity by {@link OperationService}: it is set when the\n * operation is defined and is not changed afterwards; rename the `name`.\n */\n @field({ required: true })\n code: string = '';\n\n /** Human-readable name shown in lists and pickers. */\n @field({ required: true })\n name: string = '';\n\n /**\n * Free-text grouping for lists (`fabrication`, `finishing`, `quality`).\n * Empty when the operation is not grouped.\n */\n category: string = '';\n\n /**\n * `false` once the operation is retired. A retired operation stays on the\n * routings and history that name it and is left out of pickers.\n */\n isActive: boolean = true;\n\n /**\n * Optional id of the qualification definition a worker needs to start this\n * operation: a `Qualification` from `@happyvertical/smrt-human-resources`.\n * Declared as a cross-package reference (metadata only: this package takes no\n * dependency on it and does not validate the id); an application reads it to\n * decide who may start the operation. `null` when none is required.\n */\n @crossPackageRef('@happyvertical/smrt-human-resources:Qualification', {\n nullable: true,\n })\n requiredQualificationId: string | null = null;\n\n constructor(options: OperationOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.code !== undefined) this.code = options.code;\n if (options.name !== undefined) this.name = options.name;\n if (options.category !== undefined) this.category = options.category;\n if (options.isActive !== undefined) this.isActive = options.isActive;\n if (options.requiredQualificationId !== undefined)\n this.requiredQualificationId = options.requiredQualificationId;\n }\n\n /** Operations are retired, never deleted, so history keeps resolving. */\n override async delete(): Promise<void> {\n throw new Error(\n `Operation ${this.code || this.id} cannot be deleted; retire it instead.`,\n );\n }\n}\n","/**\n * Assembly — the product that is made, the counterpart to `Material` (the\n * product that is consumed).\n *\n * A single-table-inheritance subtype of `@happyvertical/smrt-products`\n * `Product`: assembly rows live in the shared `products` table with\n * `productType: 'assembly'` and `_meta_type:\n * '@happyvertical/smrt-manufacturing:Assembly'`. Its own fields are\n * ordinary columns that this package adds to that table.\n *\n * Everything else comes from a neighbour and is deliberately not repeated\n * here:\n *\n * - name, description, price, category and tags from `Product`;\n * - part number, barcode and stock from its `Sku` (`smrt-products`) and the\n * stock levels in `smrt-inventory`;\n * - what it is made of from its {@link BillOfMaterials} and {@link BomLine}s;\n * - specs, plans and drawings from `ProductAsset` links.\n *\n * **Nesting lives in the bill, not on the assembly.** A sub-assembly is a\n * bill line whose component SKU belongs to another `Assembly`; there is no\n * children list to keep in sync. Use {@link AssemblyService.resolveComponent}\n * to tell a sub-assembly from a material or a bought item.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport {\n Product,\n type ProductOptions,\n ProductType,\n} from '@happyvertical/smrt-products/models';\nimport { TenantScoped } from '@happyvertical/smrt-tenancy';\nimport { Operation } from './Operation.js';\n\n/**\n * Options accepted by the {@link Assembly} constructor.\n */\nexport interface AssemblyOptions extends ProductOptions {\n estimatedLabourMinutes?: number;\n defaultOperationId?: string | null;\n partReference?: string;\n}\n\n// `@TenantScoped` and the generation config are registered per concrete\n// class and are not inherited from the STI parent (see `Material`): repeat\n// both so assembly reads are tenant-filtered and the REST/MCP surface\n// matches `Product` (CRUD without delete over REST, read-only over MCP)\n// instead of falling open.\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: {\n include: ['list', 'get', 'create', 'update'], // no delete, matches Product\n },\n mcp: {\n include: ['list', 'get'], // read-only AI tools, matches Product\n },\n cli: { skipApiCheck: true },\n})\nexport class Assembly extends Product { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n override productType: ProductType = ProductType.ASSEMBLY;\n\n /**\n * Labour to build one unit, in whole minutes. A planning estimate until\n * routing steps (smrt#3445) provide labour per operation; `0` means not\n * estimated.\n */\n @field()\n estimatedLabourMinutes: number = 0;\n\n /**\n * The {@link Operation} that work on this assembly is booked to by\n * default, or `null` for none. `RESTRICT`, like `RoutingStep.operationId`:\n * operations are retired, never deleted.\n */\n @foreignKey(Operation, { nullable: true, onDelete: 'RESTRICT' })\n defaultOperationId: string | null = null;\n\n /**\n * The organization's own drawing or part number for this assembly,\n * alongside (not instead of) the SKU code.\n */\n @field()\n partReference: string = '';\n\n constructor(options: AssemblyOptions = {}) {\n super(options);\n if (options.estimatedLabourMinutes !== undefined)\n this.estimatedLabourMinutes = options.estimatedLabourMinutes;\n if (options.defaultOperationId !== undefined)\n this.defaultOperationId = options.defaultOperationId;\n if (options.partReference !== undefined)\n this.partReference = options.partReference;\n }\n\n /**\n * Refuses a labour estimate that is not a whole number of minutes of zero\n * or more, on every save (generated REST and collection writes included).\n */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n const minutes = this.estimatedLabourMinutes;\n if (!Number.isSafeInteger(minutes) || minutes < 0) {\n throw ValidationError.invalidValue(\n 'estimatedLabourMinutes',\n minutes,\n 'a whole number of minutes, zero or more',\n );\n }\n }\n}\n","/**\n * AssemblyCollection — collection for {@link Assembly} rows.\n *\n * Assemblies share the `products` table with every other `Product` subtype;\n * the STI framework filters this collection to assembly rows by\n * `_meta_type`, so `list()` and `get()` only ever return assemblies.\n *\n * @packageDocumentation\n */\n\nimport { ProductCollection } from '@happyvertical/smrt-products/collections';\nimport { Assembly } from '../models/Assembly.js';\n\nexport class AssemblyCollection extends ProductCollection {\n static override readonly _itemClass = Assembly;\n}\n","/**\n * BillOfMaterials — recipe describing how to produce a finished product.\n *\n * A BOM enumerates the component SKUs (raw materials, trims, sub-assemblies)\n * that must be consumed to produce one unit of a given parent product. The\n * same product may have many BOMs — alternate revisions, alternate recipes,\n * region-specific variations — distinguished by `version` and lifecycle\n * `status`.\n *\n * Strictly industry-neutral: the same model serves apparel, furniture,\n * automotive, CPG, electronics, food production, custom hardware, and any\n * other vertical that builds finished goods from components.\n *\n * Cross-package references (`productId`) are plain string ids — never\n * `@foreignKey()` — so the schema does not couple to the catalog package's\n * table layout. The product is usually an `Assembly`.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { BomStatus } from '../types.js';\n\n/**\n * Options accepted by the {@link BillOfMaterials} constructor.\n */\nexport interface BillOfMaterialsOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n productId?: string;\n version?: number;\n effectiveDate?: Date | string | null;\n status?: BomStatus;\n notes?: string;\n currency?: string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_boms',\n // Natural key: one BOM per (product, version) per tenant. Re-saving the\n // same combination is an upsert rather than a UNIQUE violation.\n conflictColumns: ['product_id', 'version', 'tenant_id'],\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class BillOfMaterials extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the BOM is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Plain string reference to the upstream product this BOM produces.\n * Typically a `Product.id` (or any STI subtype thereof) from\n * `@happyvertical/smrt-products`, usually an `Assembly`. Cross-package\n * id, intentionally not a `@foreignKey()`.\n */\n @field({ required: true })\n productId: string = '';\n\n /**\n * Monotonically increasing revision number scoped per `productId`. Together\n * with `tenantId` and `productId` this forms the natural key — so\n * `version` is an integer and `0` is reserved by convention for unsaved\n * scratch rows.\n */\n @field({ required: true })\n version: number = 1;\n\n /**\n * Date from which this BOM is intended to take effect for new production\n * runs. Optional — older BOMs imported from legacy systems may have no\n * effective date on file.\n */\n effectiveDate: Date | null = null;\n\n /**\n * Lifecycle state — `draft`, `active`, or `superseded`. See\n * {@link BomStatus} for the semantics.\n *\n * Most queries that pick \"the BOM to use right now\" should filter for\n * `status: 'active'`.\n */\n @field({ required: true })\n status: BomStatus = 'draft';\n\n /**\n * Optional free-form notes shown in admin UIs (revision reason, source\n * document reference, sign-off chain, etc.).\n */\n notes: string = '';\n\n /**\n * ISO 4217 currency code for cost rollups. Mostly a hint for the rollup\n * service — defaults to `'USD'` so older imports without a currency\n * still produce sensible numbers.\n */\n currency: string = 'USD';\n\n constructor(options: BillOfMaterialsOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.productId !== undefined) this.productId = options.productId;\n if (options.version !== undefined) this.version = options.version;\n if (options.effectiveDate !== undefined) {\n this.effectiveDate =\n options.effectiveDate === null\n ? null\n : options.effectiveDate instanceof Date\n ? options.effectiveDate\n : new Date(options.effectiveDate);\n }\n if (options.status !== undefined) this.status = options.status;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.currency !== undefined) this.currency = options.currency;\n }\n\n /**\n * Refuses saving the bill as `active` when one of its lines would make\n * its product contain itself, directly or through sub-assemblies' active\n * bills, with a `BomCycleError` naming the path. Draft and superseded\n * bills are not part of the structure and are not checked here (their\n * lines are checked as they are saved).\n */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (this.status !== 'active') return;\n // Loaded lazily: the service imports this model through its collections.\n const { AssemblyService } = await import('../services/AssemblyService.js');\n const service = await AssemblyService.create({ db: this.db });\n await service.assertBillAcyclic(this);\n }\n}\n","/**\n * BillOfMaterialsCollection — query helpers for {@link BillOfMaterials} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomStatus } from '../types.js';\n\nexport class BillOfMaterialsCollection extends SmrtCollection<BillOfMaterials> {\n static readonly _itemClass = BillOfMaterials;\n\n /**\n * Return every BOM for the given upstream product id, newest version\n * first. Useful for \"show me all revisions of this product's recipe\"\n * admin screens.\n */\n async findByProduct(productId: string): Promise<BillOfMaterials[]> {\n return this.list({ where: { productId }, orderBy: 'version DESC' });\n }\n\n /**\n * Return the currently `active` BOM for the given product, or `null` if\n * none has been activated yet. When multiple `active` rows exist (which\n * should not happen but can arise during partial migrations), the\n * highest-version row wins.\n */\n async findActiveForProduct(\n productId: string,\n ): Promise<BillOfMaterials | null> {\n const matches = await this.list({\n where: { productId, status: 'active' },\n orderBy: 'version DESC',\n limit: 1,\n });\n return matches[0] ?? null;\n }\n\n /**\n * Return every BOM with the given lifecycle status across all products.\n * Mostly useful for admin dashboards (e.g. \"show every draft BOM\").\n */\n async findByStatus(status: BomStatus): Promise<BillOfMaterials[]> {\n return this.list({ where: { status }, orderBy: 'productId ASC' });\n }\n}\n","/**\n * BomLine — one component on a {@link BillOfMaterials}.\n *\n * Each line declares the component SKU required to produce one unit of the\n * parent product, the quantity-per-unit, the unit of measure (open-ended\n * string — `yards`, `each`, `grams`, `kg`, `m`, anything else the\n * application needs), and an optional `wastePercent` so cost rollups and\n * requirements explosions can account for offcuts, setup losses, and other\n * material consumed but not incorporated into a finished unit.\n *\n * Cross-package references (`componentSkuId`) are plain string ids — never\n * `@foreignKey()`. The `Sku` model lives in `@happyvertical/smrt-products`\n * and `@happyvertical/smrt-inventory` holds the stock levels and movements\n * addressed by the id. When the SKU belongs to an `Assembly`, the line is a\n * sub-assembly: `AssemblyService.resolveComponent` says which.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Options accepted by the {@link BomLine} constructor.\n */\nexport interface BomLineOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n bomId?: string;\n componentSkuId?: string;\n qtyPerUnit?: number;\n uom?: string;\n wastePercent?: number;\n notes?: string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_bom_lines',\n // Natural key: one row per `(bom, component)` per tenant. Re-saving the\n // same combination updates the row in place rather than violating the\n // UNIQUE constraint. Callers that want to declare two distinct lines for\n // the same component (e.g. \"main\" vs \"trim\" passes) should use distinct\n // component SKUs or model the variants in inventory.\n conflictColumns: ['bom_id', 'component_sku_id', 'tenant_id'],\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class BomLine extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the line is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Plain string reference to the parent {@link BillOfMaterials}. Within\n * this package this is the only \"structural\" foreign key — but it is\n * still stored as a plain string to keep with the package's\n * cross-reference convention and to avoid coupling the schema generator\n * to a particular load order.\n */\n @field({ required: true })\n bomId: string = '';\n\n /**\n * Plain string reference to the component SKU id (raw material, trim,\n * sub-assembly). The `Sku` model itself lives in\n * `@happyvertical/smrt-products`; `@happyvertical/smrt-inventory` uses\n * this id to track stock levels and movements. Cross-package id;\n * never `@foreignKey()`.\n */\n @field({ required: true })\n componentSkuId: string = '';\n\n /**\n * Quantity of the component required per unit of finished product, in\n * the unit declared by {@link uom}. Decimal so fractional usage like\n * `1.75 yards` or `12.5 grams` round-trips correctly.\n */\n @field({ type: 'decimal' })\n qtyPerUnit: number = 0.0;\n\n /**\n * Unit of measure for {@link qtyPerUnit}. Open-ended string so the\n * framework does not constrain the vocabulary — `'yards'`, `'each'`,\n * `'grams'`, `'kg'`, `'m'`, `'litres'`, `'sq_ft'`, anything.\n *\n * Conventions follow the upstream `Material.uom` value when applicable.\n */\n uom: string = 'each';\n\n /**\n * Expected percentage waste on this line. Adds to the effective quantity\n * consumed during cost rollups and requirements explosions so a cutting\n * yield of 10% surfaces in the total cost without callers having to\n * pre-inflate `qtyPerUnit`. Stored as a percent (`10` means 10%), not a\n * fraction (`0.10`).\n */\n @field({ type: 'decimal' })\n wastePercent: number = 0.0;\n\n /**\n * Optional free-form notes — e.g. \"primary structural component\",\n * \"60-second cure\", or \"matched batch only\".\n */\n notes: string = '';\n\n constructor(options: BomLineOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.bomId !== undefined) this.bomId = options.bomId;\n if (options.componentSkuId !== undefined)\n this.componentSkuId = options.componentSkuId;\n if (options.qtyPerUnit !== undefined) this.qtyPerUnit = options.qtyPerUnit;\n if (options.uom !== undefined) this.uom = options.uom;\n if (options.wastePercent !== undefined)\n this.wastePercent = options.wastePercent;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Effective quantity consumed per produced unit, including waste. Used\n * by {@link BomService} for cost rollups and requirements explosions.\n *\n * `effectiveQty = qtyPerUnit * (1 + wastePercent / 100)`\n */\n effectiveQtyPerUnit(): number {\n const qty = Number(this.qtyPerUnit ?? 0);\n const waste = Number(this.wastePercent ?? 0);\n return qty * (1 + waste / 100);\n }\n\n /**\n * Refuses a line that would make its bill's product contain itself,\n * directly or through sub-assemblies' active bills, with a\n * `BomCycleError` naming the path. Runs on every save, so the generated\n * REST `create`/`update` routes refuse it too.\n */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n // Loaded lazily: the service imports this model through its collections.\n const { AssemblyService } = await import('../services/AssemblyService.js');\n const service = await AssemblyService.create({ db: this.db });\n await service.assertLineAcyclic(this);\n }\n}\n","/**\n * BomLineCollection — query helpers for {@link BomLine} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { BomLine } from '../models/BomLine.js';\n\nexport class BomLineCollection extends SmrtCollection<BomLine> {\n static readonly _itemClass = BomLine;\n\n /**\n * Return every line that belongs to the given BOM. Caller-stable order\n * helps make cost rollups and requirements explosions deterministic.\n */\n async findByBom(bomId: string): Promise<BomLine[]> {\n return this.list({ where: { bomId }, orderBy: 'componentSkuId ASC' });\n }\n\n /**\n * Return every line that references the given component SKU across all\n * BOMs. Useful for \"where is this material used?\" admin screens and for\n * cost-change impact analysis (a price hike on a raw material affects\n * every BOM in the result).\n */\n async findByComponent(componentSkuId: string): Promise<BomLine[]> {\n return this.list({ where: { componentSkuId }, orderBy: 'bomId ASC' });\n }\n}\n","/**\n * OperationCollection — query helpers for {@link Operation} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Operation } from '../models/Operation.js';\n\nexport class OperationCollection extends SmrtCollection<Operation> {\n static readonly _itemClass = Operation;\n\n /** Return the operation with the given code, or `null`. */\n async findByCode(code: string): Promise<Operation | null> {\n const matches = await this.list({ where: { code }, limit: 1 });\n return matches[0] ?? null;\n }\n\n /**\n * Return operations ordered by category then code. Retired operations are\n * left out unless `includeRetired` is set, so the default is a picker list.\n */\n async listOperations(\n options: { includeRetired?: boolean } = {},\n ): Promise<Operation[]> {\n return this.list({\n where: options.includeRetired ? {} : { isActive: true },\n orderBy: ['category ASC', 'code ASC'],\n });\n }\n}\n","/**\n * Quantity precision for planning and production runs.\n *\n * Quantities are decimals held in binary floating point, so sums such as\n * `0.1 + 0.2` and products such as `2.2 * 100` carry rounding noise. Rather\n * than compare with a tolerance (which must grow with the value and so\n * eventually hides a whole unit), quantities are kept to\n * {@link QUANTITY_DECIMALS} decimal places: rounded through their decimal\n * representation, and summed and compared exactly as whole millionths.\n *\n * @packageDocumentation\n */\n\n/** Decimal places a quantity is kept to: a millionth of a unit. */\nexport const QUANTITY_DECIMALS = 6;\n\nconst SCALE = 10 ** QUANTITY_DECIMALS;\n\n/**\n * The largest quantity a production run accepts (target or report): every\n * value up to it with {@link QUANTITY_DECIMALS} places has at most 15\n * significant digits, so it survives a round trip through a double exactly\n * and sums of them stay exact.\n */\nexport const MAX_QUANTITY = 999_999_999;\n\n/**\n * `value` rounded to {@link QUANTITY_DECIMALS} decimal places (correctly\n * rounded through its decimal representation, so `0.1 + 0.2` becomes\n * exactly `0.3`).\n */\nexport function roundQuantity(value: number): number {\n return Number(value.toFixed(QUANTITY_DECIMALS));\n}\n\n/**\n * `value` as a whole number of millionths, exactly (read from its decimal\n * representation, not multiplied). Exact for any value up to\n * {@link MAX_QUANTITY}.\n */\nexport function toMillionths(value: number): number {\n return Number(value.toFixed(QUANTITY_DECIMALS).replace('.', ''));\n}\n\n/** A whole number of millionths back as a quantity. */\nexport function fromMillionths(millionths: number): number {\n return millionths / SCALE;\n}\n","/**\n * ProductionRun — a build of some quantity of a product against one\n * {@link BillOfMaterials}, reported as it happens: \"25 to build, 12 done\".\n *\n * The run pins the bill it was started against, carries the target and the\n * completed quantity, and moves through `planned` → `in_progress` → `done`,\n * or to `cancelled`. Each report of finished units is a dated\n * {@link ProductionRunCompletion}; `completedQty` is their sum.\n *\n * Writes go through `ProductionRunService`, which keeps `completedQty` equal\n * to the completions under concurrent reporting and enforces the status\n * transitions. The generated REST/MCP/CLI surfaces are read-only for that\n * reason.\n *\n * This is the shop-floor record of building. It is not the commercial\n * instruction to make goods (`ProductionOrder` in `@happyvertical/smrt-commerce`,\n * which carries terms and money); several runs may serve one order, or none\n * (make to stock). An application that has both links them itself.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { roundQuantity } from '../quantity.js';\nimport { BillOfMaterials } from './BillOfMaterials.js';\n\n/**\n * Where a {@link ProductionRun} is:\n *\n * - `planned` — created, nothing reported yet.\n * - `in_progress` — started, or at least one completion reported.\n * - `done` — the target was reached, or the run was finished short.\n * - `cancelled` — stopped; completions already reported stay.\n */\nexport type ProductionRunStatus =\n | 'planned'\n | 'in_progress'\n | 'done'\n | 'cancelled';\n\n/** Every {@link ProductionRunStatus}, in lifecycle order. */\nexport const PRODUCTION_RUN_STATUSES: readonly ProductionRunStatus[] = [\n 'planned',\n 'in_progress',\n 'done',\n 'cancelled',\n];\n\n/**\n * Options accepted by the {@link ProductionRun} constructor.\n */\nexport interface ProductionRunOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n bomId?: string;\n targetQty?: number;\n completedQty?: number;\n status?: ProductionRunStatus;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_production_runs',\n // Writes go through ProductionRunService, which serializes completions and\n // enforces the status transitions; generated create/update would bypass it.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class ProductionRun extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the run is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The bill the run builds against, pinned when the run is created.\n * Restrict: a bill with runs is history and is not deleted.\n */\n @foreignKey(BillOfMaterials, { required: true, onDelete: 'RESTRICT' })\n bomId: string = '';\n\n /** Units of the bill's product to build. Decimal; greater than zero. */\n @field({ type: 'decimal' })\n targetQty: number = 0.0;\n\n /** Units reported done so far: the sum of the run's completions. */\n @field({ type: 'decimal' })\n completedQty: number = 0.0;\n\n /** Lifecycle status; see {@link ProductionRunStatus}. */\n @field({ required: true })\n status: ProductionRunStatus = 'planned';\n\n constructor(options: ProductionRunOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.bomId !== undefined) this.bomId = options.bomId;\n if (options.targetQty !== undefined) this.targetQty = options.targetQty;\n if (options.completedQty !== undefined)\n this.completedQty = options.completedQty;\n if (options.status !== undefined) this.status = options.status;\n }\n\n /**\n * Units still to build, at the six-decimal quantity precision; `0` once\n * the target is reached.\n */\n remainingQty(): number {\n return Math.max(\n 0,\n roundQuantity(Number(this.targetQty) - Number(this.completedQty)),\n );\n }\n\n /** Refuses a status outside the lifecycle and impossible quantities. */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (!PRODUCTION_RUN_STATUSES.includes(this.status))\n throw new ValidationError(\n `Unknown production run status: ${String(this.status)}`,\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'status' },\n );\n const target = Number(this.targetQty);\n const completed = Number(this.completedQty);\n if (!Number.isFinite(target) || target <= 0)\n throw new ValidationError(\n 'A production run needs a target quantity greater than zero.',\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'targetQty' },\n );\n if (!Number.isFinite(completed) || completed < 0)\n throw new ValidationError(\n 'A production run cannot have a negative completed quantity.',\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'completedQty' },\n );\n }\n}\n","/**\n * ProductionRunCollection — query helpers for {@link ProductionRun} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n ProductionRun,\n type ProductionRunStatus,\n} from '../models/ProductionRun.js';\n\nexport class ProductionRunCollection extends SmrtCollection<ProductionRun> {\n static readonly _itemClass = ProductionRun;\n\n /** Runs against one bill, newest first. */\n async findByBom(bomId: string): Promise<ProductionRun[]> {\n return this.list({ where: { bomId }, orderBy: 'created_at DESC' });\n }\n\n /** Runs in one status, oldest first (a work queue). */\n async findByStatus(status: ProductionRunStatus): Promise<ProductionRun[]> {\n return this.list({ where: { status }, orderBy: 'created_at ASC' });\n }\n}\n","/**\n * ProductionRunCompletion — one dated report of finished units on a\n * {@link ProductionRun} (\"12 done on Tuesday\").\n *\n * Written only by `ProductionRunService.recordCompletion`, in the same\n * transaction that adds `qty` to the run's `completedQty` and, when the\n * caller asks, posts the stock movements; those movements carry\n * `sourceType: 'ProductionRunCompletion'` and this row's id. Read-only over\n * the generated surfaces.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { ProductionRun } from './ProductionRun.js';\n\n/**\n * Options accepted by the {@link ProductionRunCompletion} constructor.\n */\nexport interface ProductionRunCompletionOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n runId?: string;\n qty?: number;\n completedAt?: Date | string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_production_run_completions',\n // Written only by ProductionRunService.recordCompletion.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class ProductionRunCompletion extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The run the units were built on. Cascade: completions belong to their run. */\n @foreignKey(ProductionRun, { required: true, onDelete: 'CASCADE' })\n runId: string = '';\n\n /** Units finished in this report. Decimal; greater than zero. */\n @field({ type: 'decimal' })\n qty: number = 0.0;\n\n /** When the units were finished (defaults to when the report was made). */\n completedAt: Date = new Date();\n\n constructor(options: ProductionRunCompletionOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.runId !== undefined) this.runId = options.runId;\n if (options.qty !== undefined) this.qty = options.qty;\n if (options.completedAt !== undefined)\n this.completedAt =\n options.completedAt instanceof Date\n ? options.completedAt\n : new Date(options.completedAt);\n }\n\n /** Refuses a report of zero, negative or non-finite units. */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n const qty = Number(this.qty);\n if (!Number.isFinite(qty) || qty <= 0)\n throw new ValidationError(\n 'A completion needs a quantity greater than zero.',\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'qty' },\n );\n }\n}\n","/**\n * ProductionRunCompletionCollection — query helpers for\n * {@link ProductionRunCompletion} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ProductionRunCompletion } from '../models/ProductionRunCompletion.js';\n\nexport class ProductionRunCompletionCollection extends SmrtCollection<ProductionRunCompletion> {\n static readonly _itemClass = ProductionRunCompletion;\n\n /** A run's completions in the order they happened. */\n async findByRun(runId: string): Promise<ProductionRunCompletion[]> {\n return this.list({\n where: { runId },\n orderBy: ['completedAt ASC', 'created_at ASC'],\n });\n }\n}\n","/**\n * RoutingStep — one operation in the routing of a {@link BillOfMaterials}.\n *\n * A routing is the ordered list of steps for building one unit of the bill's\n * product. It is optional: a bill with no routing steps behaves exactly as a\n * bill always has. Steps are ordered by `sequence`, unique within the bill.\n *\n * Cross-package-style references (`bomId`, `operationId`) are plain string ids,\n * as elsewhere in this package.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterials } from './BillOfMaterials.js';\nimport { Operation } from './Operation.js';\n\n/**\n * Options accepted by the {@link RoutingStep} constructor.\n */\nexport interface RoutingStepOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n bomId?: string;\n operationId?: string;\n sequence?: number;\n estimatedMinutes?: number;\n notes?: string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_routing_steps',\n // One step per position per bill per tenant.\n conflictColumns: ['bom_id', 'sequence', 'tenant_id'],\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class RoutingStep extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the step is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The owning {@link BillOfMaterials}. Cascade: a routing belongs to its bill.\n * (`BomLine.bomId` is still a plain string; see AGENTS.md.)\n */\n @foreignKey(BillOfMaterials, { required: true, onDelete: 'CASCADE' })\n bomId: string = '';\n\n /** The {@link Operation} performed here. Restrict: operations are retired, never deleted. */\n @foreignKey(Operation, { required: true, onDelete: 'RESTRICT' })\n operationId: string = '';\n\n /** Position in the routing, starting at 1 and without gaps. */\n @field({ required: true })\n sequence: number = 1;\n\n /**\n * Estimated working minutes at this step to build one unit of the bill's\n * product. Decimal so `7.5` round-trips.\n */\n @field({ type: 'decimal' })\n estimatedMinutes: number = 0.0;\n\n /** Optional free-form note for this step. */\n notes: string = '';\n\n constructor(options: RoutingStepOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.bomId !== undefined) this.bomId = options.bomId;\n if (options.operationId !== undefined)\n this.operationId = options.operationId;\n if (options.sequence !== undefined) this.sequence = options.sequence;\n if (options.estimatedMinutes !== undefined)\n this.estimatedMinutes = options.estimatedMinutes;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n}\n","/**\n * RoutingStepCollection — query helpers for {@link RoutingStep} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { RoutingStep } from '../models/RoutingStep.js';\n\nexport class RoutingStepCollection extends SmrtCollection<RoutingStep> {\n static readonly _itemClass = RoutingStep;\n\n /** Return the routing of a bill in step order; empty when it has none. */\n async findByBom(bomId: string): Promise<RoutingStep[]> {\n return this.list({ where: { bomId }, orderBy: 'sequence ASC' });\n }\n\n /** Return every step that uses the given operation, across bills. */\n async findByOperation(operationId: string): Promise<RoutingStep[]> {\n return this.list({ where: { operationId }, orderBy: 'bomId ASC' });\n }\n}\n","/**\n * Types and error classes for operations, routing and the labour estimate.\n *\n * Strictly industry-neutral.\n *\n * @packageDocumentation\n */\n\nimport type { Operation } from './models/Operation.js';\nimport type { SubAssemblyLabour } from './types.js';\n\n/**\n * Callback that returns the hourly labour rate for an operation, in the bill's\n * currency, or `null` / `undefined` when no rate is known. The package holds no\n * rates: the caller decides where they come from (a price list, a rate class\n * the application keeps against the operation code, a timesheet rate). Mirrors\n * `ComponentCostResolver` for materials.\n */\nexport type OperationRateResolver = (\n operation: Operation,\n) => Promise<number | null | undefined> | number | null | undefined;\n\n/** One step of {@link LabourEstimate}. */\nexport interface RoutingStepEstimate {\n /** The routing step id. */\n stepId: string;\n /** Position in the routing, starting at 1. */\n sequence: number;\n /** Plain string id of the operation. */\n operationId: string;\n /** Operation code, or `''` if the operation row is missing. */\n operationCode: string;\n /** Operation name, or `''` if the operation row is missing. */\n operationName: string;\n /** Estimated working minutes per produced unit. */\n estimatedMinutes: number;\n /** Hourly rate from the resolver, or `0` when unavailable. */\n hourlyRate: number;\n /** `estimatedMinutes / 60 * hourlyRate`; `0` when the rate is unavailable. */\n stepCost: number;\n /** `true` when no resolver was supplied or it returned no rate. */\n rateUnavailable: boolean;\n}\n\n/**\n * Estimated labour per produced unit, returned by\n * `BomService.computeLabourEstimate`. A bill with no routing returns an empty\n * `steps` array and zero totals.\n */\nexport interface LabourEstimate {\n /** The bill that was rolled up. */\n bomId: string;\n /** `true` when the bill has at least one routing step. */\n hasRouting: boolean;\n /** Total estimated working minutes per produced unit. */\n totalMinutes: number;\n /** Total estimated labour cost per unit; counts only steps with a rate. */\n totalCost: number;\n /** ISO 4217 currency code of the bill. */\n currency: string;\n /** Steps in routing order. */\n steps: RoutingStepEstimate[];\n /** `true` when a step's rate was unavailable, so `totalCost` is a lower bound. */\n hasMissingRates: boolean;\n /**\n * Multi-level only (`{ levels }` above 1): every sub-assembly the walk\n * opened, at any depth, with its own routing estimate and how many units of\n * it one unit of this bill's product needs. `totalMinutes`, `totalCost`,\n * `hasRouting` and `hasMissingRates` then cover them too; `steps` stays this\n * bill's own routing.\n */\n subAssemblies?: SubAssemblyLabour[];\n}\n\n/** One step handed to `RoutingService.replaceRouting`. */\nexport interface RoutingStepInput {\n /** Plain string id of the operation to perform. */\n operationId: string;\n /** Estimated working minutes per produced unit; zero or more. */\n estimatedMinutes: number;\n /** Optional free-form note. */\n notes?: string;\n}\n\n/** Input for `OperationService.define`. */\nexport interface DefineOperationInput {\n /** Unique per tenant; not changed afterwards. */\n code: string;\n name: string;\n category?: string;\n /** Plain string id of a `smrt-human-resources` qualification definition. */\n requiredQualificationId?: string;\n}\n\n/** Thrown when an operation id does not exist. */\nexport class OperationNotFoundError extends Error {\n override name = 'OperationNotFoundError';\n\n constructor(public readonly operationId: string) {\n super(`Operation not found: ${operationId}`);\n }\n}\n\n/** Thrown when defining an operation whose code is already in use. */\nexport class DuplicateOperationCodeError extends Error {\n override name = 'DuplicateOperationCodeError';\n\n constructor(public readonly code: string) {\n super(`An operation with code \"${code}\" already exists.`);\n }\n}\n\n/** Thrown when a routing would newly add a retired operation. */\nexport class OperationRetiredError extends Error {\n override name = 'OperationRetiredError';\n\n constructor(public readonly operationId: string) {\n super(\n `Operation ${operationId} is retired and cannot be added to a routing.`,\n );\n }\n}\n\n/** Thrown for invalid operation or routing input. */\nexport class InvalidOperationInputError extends Error {\n override name = 'InvalidOperationInputError';\n}\n","/**\n * Inputs, results and errors for `ProductionRunService`.\n *\n * @packageDocumentation\n */\n\nimport type { ProductionRun } from './models/ProductionRun.js';\nimport type { ProductionRunCompletion } from './models/ProductionRunCompletion.js';\nimport type {\n ConsumeResult,\n ProduceResult,\n} from './services/ProductionService.js';\n\n/** Input for `ProductionRunService.createRun`. Give `bomId`, or `productId` for its active bill. */\nexport interface CreateProductionRunInput {\n /** The bill to build against. Wins over `productId`. */\n bomId?: string;\n /** A product (usually an `Assembly`) whose active bill the run pins. */\n productId?: string;\n /** Units to build; greater than zero. */\n targetQty: number;\n}\n\n/** Stock to consume when a completion is recorded: the run's bill, for the completed units. */\nexport interface CompletionConsumeOptions {\n /** Location the components are taken from. */\n locationId: string;\n /** Optional free-form note on each movement. */\n note?: string;\n}\n\n/** Finished goods to receive when a completion is recorded. */\nexport interface CompletionProduceOptions {\n /** Location the finished units are received into. */\n locationId: string;\n /** The SKU of the bill's product that was built. */\n finishedSkuId: string;\n /** Optional free-form note on the movement. */\n note?: string;\n}\n\n/** Input for `ProductionRunService.recordCompletion`. */\nexport interface RecordCompletionInput {\n /** Units finished; greater than zero and no more than the run has left. */\n qty: number;\n /** When they were finished. Defaults to now. */\n completedAt?: Date | string;\n /**\n * Consume the run's bill for `qty` units, in the same transaction. Omit\n * (the default) to record progress without touching stock.\n */\n consume?: CompletionConsumeOptions;\n /**\n * Receive `qty` finished units, in the same transaction. Omit (the\n * default) to record progress without touching stock.\n */\n produce?: CompletionProduceOptions;\n}\n\n/** Result of `ProductionRunService.recordCompletion`. */\nexport interface RecordCompletionResult {\n /** The run after the completion. */\n run: ProductionRun;\n /** The completion that was recorded. */\n completion: ProductionRunCompletion;\n /** Components consumed; empty unless `consume` was given. */\n consumed: ConsumeResult[];\n /** Finished goods received; `null` unless `produce` was given. */\n produced: ProduceResult | null;\n}\n\n/** Thrown when a production run id does not exist. */\nexport class ProductionRunNotFoundError extends Error {\n override name = 'ProductionRunNotFoundError';\n\n constructor(public readonly runId: string) {\n super(`Production run not found: ${runId}`);\n }\n}\n\n/** Thrown when an action is not allowed in the run's current status. */\nexport class ProductionRunStateError extends Error {\n override name = 'ProductionRunStateError';\n\n constructor(\n public readonly runId: string,\n /** The run's status when the action was refused. */\n public readonly status: string,\n /** What was attempted: `start`, `complete`, `finish`, `cancel`, `retarget`. */\n public readonly action: string,\n ) {\n super(`Production run ${runId} is ${status}; cannot ${action} it.`);\n }\n}\n\n/** Thrown when a completion would take the run past its target. */\nexport class ProductionRunOverCompletionError extends Error {\n override name = 'ProductionRunOverCompletionError';\n\n constructor(\n public readonly runId: string,\n /** The quantity reported. */\n public readonly qty: number,\n /** What the run had left to build. */\n public readonly remaining: number,\n ) {\n super(\n `Production run ${runId} has ${remaining} left to build; cannot report ${qty}.`,\n );\n }\n}\n\n/** Thrown for invalid production run input (quantities, dates, bill). */\nexport class InvalidProductionRunInputError extends Error {\n override name = 'InvalidProductionRunInputError';\n}\n","/**\n * Reading a structure in its owner's tenant: the owner's rows and global\n * ones, whatever tenant context is active.\n *\n * Under a tenant context the tenancy interceptor narrows every read to that\n * tenant, which hides global rows (a shared material, a global bill, global\n * stock); without a context it narrows nothing, which shows every tenant's\n * rows. Neither is a bill's structure. {@link readOwnAndGlobal} runs list\n * reads through `withTenantGlobalRead` (tenant rows plus global rows, refused\n * for a tenant the active context may not read), and callers keep\n * {@link isOwnOrGlobal} rows, which also covers tenancy being disabled.\n *\n * @packageDocumentation\n */\n\nimport {\n getCurrentTenant,\n withTenantGlobalRead,\n} from '@happyvertical/smrt-tenancy';\n\n/**\n * Run `read` so its list reads return `owner`'s rows and global ones. For a\n * global owner (`null`), reads see the active tenant's rows and global ones,\n * and callers keep the global rows.\n */\nexport function readOwnAndGlobal<T>(\n owner: string | null,\n read: () => Promise<T>,\n): Promise<T> {\n const scope = owner ?? getCurrentTenant()?.tenantId ?? null;\n return scope ? withTenantGlobalRead(scope, read) : read();\n}\n\n/** `true` for a global row or one of `owner`'s rows. */\nexport function isOwnOrGlobal(\n row: { tenantId?: string | null },\n owner: string | null,\n): boolean {\n const rowTenant = row.tenantId ?? null;\n return rowTenant === null || rowTenant === owner;\n}\n","/**\n * AssemblyService — the link between a bill line and the thing it names.\n *\n * A {@link BomLine} points at a component SKU. That SKU belongs to a\n * `Product` row in `@happyvertical/smrt-products`, and the row's subtype\n * decides what the line means:\n *\n * | Component SKU belongs to | Kind | Meaning |\n * |---|---|---|\n * | an {@link Assembly} | `assembly` | a sub-assembly; it is built from its own active bill, if it has one |\n * | a `Material` | `material` | a consumed input; the leaf of the structure |\n * | any other `Product` | `bought` | an item that is bought, not built here |\n * | nothing (unknown SKU or product) | `missing` | the line points at a record that does not exist |\n *\n * {@link AssemblyService.resolveComponent} answers that question for one\n * SKU, and also returns the assembly's active bill. It is the step a\n * multi-level explosion repeats: resolve each line's component, recurse into\n * every `assembly` that has an `activeBom`, stop at everything else.\n *\n * The service also owns the **cycle refusal**: an assembly may not contain\n * itself, directly or through sub-assemblies. {@link BomLine} and\n * {@link BillOfMaterials} call {@link AssemblyService.assertLineAcyclic} and\n * {@link AssemblyService.assertBillAcyclic} before every save, so the\n * generated REST routes refuse a cycle too.\n *\n * @packageDocumentation\n */\n\nimport {\n type DatabaseConfig,\n resolveDatabase,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport {\n ProductCollection,\n SkuCollection,\n} from '@happyvertical/smrt-products/collections';\nimport {\n Material,\n type Product,\n type Sku,\n} from '@happyvertical/smrt-products/models';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { BomLineCollection } from '../collections/BomLineCollection.js';\nimport { Assembly } from '../models/Assembly.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport { readOwnAndGlobal } from '../tenant-scope.js';\nimport { BomNotFoundError } from '../types.js';\n\n/**\n * What a component SKU is, as far as building goes. See the table in the\n * {@link AssemblyService} module documentation.\n */\nexport type ComponentKind = 'assembly' | 'material' | 'bought' | 'missing';\n\n/**\n * The answer to \"what is this component SKU?\" returned by\n * {@link AssemblyService.resolveComponent}.\n */\nexport interface ResolvedComponent {\n /** The component SKU id that was resolved. */\n skuId: string;\n /** What the SKU is; see {@link ComponentKind}. */\n kind: ComponentKind;\n /** The SKU row, or `null` when no SKU has this id. */\n sku: Sku | null;\n /**\n * The product the SKU belongs to (an `Assembly`, a `Material` or a plain\n * `Product`), or `null` when the SKU or its product does not exist.\n */\n product: Product | null;\n /** The assembly, when {@link kind} is `assembly`; otherwise `null`. */\n assembly: Assembly | null;\n /**\n * The assembly's active bill (the highest active version), or `null` for\n * an assembly that has no active bill yet and for every other kind.\n */\n activeBom: BillOfMaterials | null;\n /**\n * `true` only for an assembly with an active bill: the component *can* be\n * built here. Whether a shortage is built or bought is the application's\n * decision; this only reports the fact.\n */\n buildable: boolean;\n}\n\n/** One bill line together with the component it resolves to. */\nexport interface BillStructureLine {\n /** The bill line as stored. */\n line: BomLine;\n /** The line's component SKU, resolved. */\n component: ResolvedComponent;\n}\n\n/**\n * One level of an assembly's structure, returned by\n * {@link AssemblyService.getBillStructure}. Sub-assembly lines carry their\n * own `activeBom`; pass its id back to `getBillStructure` to open the next\n * level.\n */\nexport interface BillStructure {\n /** The bill that was read. */\n bom: BillOfMaterials;\n /** Its lines in component-SKU order, each resolved. */\n lines: BillStructureLine[];\n}\n\n/** One product on a cycle path reported by {@link BomCycleError}. */\nexport interface BomCyclePathEntry {\n /** The product id (an assembly, or the bill's own product). */\n productId: string;\n /** The product's name, or its id when it has no name or cannot be read. */\n name: string;\n}\n\n/**\n * Thrown when saving a bill line, or saving a bill as active, would make a\n * product contain itself. `path` starts and ends with the bill's product,\n * listing each assembly in between: `Frame → Side panel → Frame`.\n */\nexport class BomCycleError extends ValidationError {\n override name = 'BomCycleError';\n\n constructor(\n /** The bill being saved (or the bill of the line being saved). */\n public readonly bomId: string,\n /** The cycle, from the bill's product back to itself. */\n public readonly path: BomCyclePathEntry[],\n ) {\n const first = path[0]?.name ?? '';\n super(\n `Refused: \"${first}\" would contain itself: ${path\n .map((entry) => entry.name)\n .join(' → ')}`,\n 'MANUFACTURING_BOM_CYCLE',\n { bomId, path },\n );\n }\n}\n\n/** Options for {@link AssemblyService.resolveComponent} and the walks built on it. */\nexport interface ResolveComponentOptions {\n /**\n * The tenant whose structure is being read. When given (including `null`\n * for global records), an assembly's active bill is chosen only among\n * bills with exactly this `tenantId`, and a SKU, product or bill line\n * belonging to another tenant is treated as missing (global rows stay\n * visible), whatever tenant context is active. When omitted, the active\n * tenant context decides what is visible.\n */\n tenantId?: string | null;\n}\n\n/** Options accepted by {@link AssemblyService.create}. */\nexport interface AssemblyServiceOptions {\n /** Database config or live handle shared by every collection. */\n db: DatabaseConfig;\n}\n\n// Ids are UUIDs on every adapter. A value that is not one cannot name a row,\n// and passing it to a UUID column on PostgreSQL is a cast error, so it is\n// resolved as `missing` without a query.\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Whether a row belongs to the structure being read: with an explicit\n * `tenantId`, a global row (`tenantId` null) or one of that tenant's rows;\n * without one, whatever the active tenant context returned.\n */\nfunction visibleTo(\n row: { tenantId?: string | null },\n options: ResolveComponentOptions,\n): boolean {\n if (options.tenantId === undefined) return true;\n const rowTenant = row.tenantId ?? null;\n return rowTenant === null || rowTenant === options.tenantId;\n}\n\nfunction isUuid(value: string | null | undefined): value is string {\n return typeof value === 'string' && UUID_PATTERN.test(value);\n}\n\n/**\n * Resolves components and refuses cycles. Construct with\n * {@link AssemblyService.create} or {@link createAssemblyService}.\n *\n * @example\n * ```typescript\n * const assemblies = await AssemblyService.create({ db });\n * const component = await assemblies.resolveComponent(line.componentSkuId);\n * if (component.kind === 'assembly' && component.activeBom) {\n * // a sub-assembly that can be built: walk component.activeBom next\n * }\n * ```\n */\nexport class AssemblyService {\n private constructor(\n /** Every `Product` row, any subtype. */\n public readonly products: ProductCollection,\n /** The SKU catalog. */\n public readonly skus: SkuCollection,\n /** Bills of materials. */\n public readonly boms: BillOfMaterialsCollection,\n /** Bill lines. */\n public readonly lines: BomLineCollection,\n ) {}\n\n /** Factory — prefer {@link createAssemblyService}. */\n static async create(\n options: AssemblyServiceOptions,\n ): Promise<AssemblyService> {\n // Resolve once so every collection shares one connection: separate\n // `create` calls with a config object such as `:memory:` would each open\n // their own database.\n const db = (await resolveDatabase(options.db)) as unknown as DatabaseConfig;\n const [products, skus, boms, lines] = await Promise.all([\n ProductCollection.create({ db }),\n SkuCollection.create({ db }),\n BillOfMaterialsCollection.create({ db }),\n BomLineCollection.create({ db }),\n ]);\n return new AssemblyService(products, skus, boms, lines);\n }\n\n /**\n * Resolve a component SKU to what it is: an assembly (with its active bill,\n * or `null` when it has none), a material, a bought item, or missing. Never\n * throws for an unknown id; it reports `missing` instead.\n *\n * This is the documented link a multi-level explosion walks: recurse into\n * `activeBom` while `kind === 'assembly'`, stop at every other kind. Pass\n * the parent bill's `tenantId` so a shared assembly resolves to that\n * tenant's bill, not another tenant's.\n */\n async resolveComponent(\n skuId: string,\n options: ResolveComponentOptions = {},\n ): Promise<ResolvedComponent> {\n const missing = (sku: Sku | null): ResolvedComponent => ({\n skuId,\n kind: 'missing',\n sku,\n product: null,\n assembly: null,\n activeBom: null,\n buildable: false,\n });\n\n if (!isUuid(skuId)) return missing(null);\n // With an explicit tenant, the SKU and product are read as that tenant's\n // or global (a shared material stays visible under a tenant context); one\n // of another tenant is reported as missing, never resolved.\n const sku = await this.byId(this.skus, skuId, options);\n if (!sku || !visibleTo(sku, options)) return missing(null);\n if (!isUuid(sku.productId)) return missing(sku);\n const product = await this.byId(this.products, sku.productId, options);\n if (!product || !visibleTo(product, options)) return missing(sku);\n\n if (product instanceof Assembly) {\n const activeBom = await this.findActiveBom(product.id!, options);\n return {\n skuId,\n kind: 'assembly',\n sku,\n product,\n assembly: product,\n activeBom,\n buildable: activeBom !== null,\n };\n }\n\n return {\n skuId,\n kind: product instanceof Material ? 'material' : 'bought',\n sku,\n product,\n assembly: null,\n activeBom: null,\n buildable: false,\n };\n }\n\n /**\n * The highest-version active bill for a product. With `options.tenantId`,\n * that tenant's own bill, or else a global one; another tenant's never.\n */\n async findActiveBom(\n productId: string,\n options: ResolveComponentOptions = {},\n ): Promise<BillOfMaterials | null> {\n const owner = options.tenantId;\n if (owner === undefined) return this.boms.findActiveForProduct(productId);\n const active = await readOwnAndGlobal(owner, () =>\n this.boms.list({\n where: { productId, status: 'active' },\n orderBy: 'version DESC',\n }),\n );\n return (\n active.find((bom) => (bom.tenantId ?? null) === owner) ??\n active.find((bom) => (bom.tenantId ?? null) === null) ??\n null\n );\n }\n\n /**\n * One row by id: through the collection's `get` without an explicit\n * tenant, else as a list read of that tenant's and global rows.\n */\n private async byId<T extends { tenantId?: string | null }>(\n collection: {\n get(filter: { id: string }): Promise<T | null>;\n list(options: { where: { id: string }; limit: number }): Promise<T[]>;\n },\n id: string,\n options: ResolveComponentOptions,\n ): Promise<T | null> {\n const owner = options.tenantId;\n if (owner === undefined) return collection.get({ id });\n const rows = await readOwnAndGlobal(owner, () =>\n collection.list({ where: { id }, limit: 1 }),\n );\n return rows[0] ?? null;\n }\n\n /** `true` when the SKU belongs to an {@link Assembly}. */\n async isAssembly(skuId: string): Promise<boolean> {\n return (await this.resolveComponent(skuId)).kind === 'assembly';\n }\n\n /**\n * Read one bill and resolve every line's component. Sub-assembly lines say\n * whether they have an active bill of their own (`component.activeBom`);\n * the caller opens the next level by passing that id back in.\n *\n * @throws {BomNotFoundError} when no bill has this id.\n */\n async getBillStructure(bomId: string): Promise<BillStructure> {\n const bom = isUuid(bomId) ? await this.boms.get({ id: bomId }) : null;\n if (!bom) throw new BomNotFoundError(bomId);\n const cache = new Map<string, Promise<ResolvedComponent>>();\n const scope = { tenantId: bom.tenantId ?? null };\n const lines = (\n await readOwnAndGlobal(scope.tenantId, () => this.lines.findByBom(bomId))\n ).filter((line) => visibleTo(line, scope));\n const resolved: BillStructureLine[] = [];\n for (const line of lines) {\n resolved.push({\n line,\n component: await this.resolveCached(line.componentSkuId, cache, scope),\n });\n }\n return { bom, lines: resolved };\n }\n\n /**\n * Find the path by which adding `componentSkuIds` to a bill for\n * `productId` would make that product contain itself, or `null` when none\n * would. The walk follows each sub-assembly's **active** bill only; a\n * draft or superseded bill is not part of the structure. Pass the bill's\n * `tenantId` so the walk reads that tenant's structure.\n */\n async findCycle(\n productId: string,\n componentSkuIds: readonly string[],\n options: ResolveComponentOptions = {},\n ): Promise<BomCyclePathEntry[] | null> {\n const cache = new Map<string, Promise<ResolvedComponent>>();\n const visited = new Set<string>();\n\n const walk = async (\n skuIds: readonly string[],\n trail: Product[],\n ): Promise<Product[] | null> => {\n for (const skuId of skuIds) {\n const component = await this.resolveCached(skuId, cache, options);\n const product = component.product;\n if (!product?.id) continue;\n if (product.id === productId) return [...trail, product];\n if (!component.activeBom || visited.has(product.id)) continue;\n visited.add(product.id);\n const billId = component.activeBom.id!;\n const lines = (\n options.tenantId === undefined\n ? await this.lines.findByBom(billId)\n : await readOwnAndGlobal(options.tenantId, () =>\n this.lines.findByBom(billId),\n )\n ).filter((line) => visibleTo(line, options));\n const found = await walk(\n lines.map((line) => line.componentSkuId),\n [...trail, product],\n );\n if (found) return found;\n }\n return null;\n };\n\n const trail = await walk(componentSkuIds, []);\n if (!trail) return null;\n const self = trail[trail.length - 1];\n return [self, ...trail].map((product) => ({\n productId: product.id!,\n name: product.name || product.id!,\n }));\n }\n\n /**\n * Refuse a bill line that would make its bill's product contain itself.\n * A line on a draft bill is checked too, so a cycle is refused when it is\n * written rather than when the bill is activated. A line whose bill does\n * not exist is left to the caller.\n *\n * @throws {BomCycleError} naming the path.\n */\n async assertLineAcyclic(\n line: Pick<BomLine, 'bomId' | 'componentSkuId'>,\n ): Promise<void> {\n if (!isUuid(line.bomId)) return;\n const bom = await this.boms.get({ id: line.bomId });\n if (!bom) return;\n const path = await this.findCycle(bom.productId, [line.componentSkuId], {\n tenantId: bom.tenantId ?? null,\n });\n if (path) throw new BomCycleError(line.bomId, path);\n }\n\n /**\n * Refuse saving a bill as `active` when any of its lines would make its\n * product contain itself. Bills in any other status are not part of the\n * structure and pass.\n *\n * The lines checked are the bill's own and those of any stored bill with\n * the same natural key (`productId`, `version`, and the bill's tenant, or\n * the active tenant when the bill has none yet): saving a new\n * instance with that key upserts onto the stored bill and activates its\n * lines, so they are part of what the save makes active.\n *\n * @throws {BomCycleError} naming the path.\n */\n async assertBillAcyclic(\n bom: Pick<\n BillOfMaterials,\n 'id' | 'productId' | 'status' | 'version' | 'tenantId'\n >,\n ): Promise<void> {\n if (bom.status !== 'active') return;\n const billIds = new Set<string>();\n if (isUuid(bom.id)) billIds.add(bom.id);\n // Validation runs before the tenancy interceptor fills `tenantId`, so an\n // omitted tenant is the one the save will be stamped with.\n const tenantId = bom.tenantId ?? getCurrentTenant()?.tenantId ?? null;\n const sameKey = await readOwnAndGlobal(tenantId, () =>\n this.boms.list({\n where: { productId: bom.productId, version: bom.version },\n }),\n );\n for (const stored of sameKey) {\n if (stored.id && (stored.tenantId ?? null) === tenantId)\n billIds.add(stored.id);\n }\n const componentSkuIds: string[] = [];\n for (const billId of billIds) {\n for (const line of await readOwnAndGlobal(tenantId, () =>\n this.lines.findByBom(billId),\n ))\n if (visibleTo(line, { tenantId }))\n componentSkuIds.push(line.componentSkuId);\n }\n if (componentSkuIds.length === 0) return;\n const path = await this.findCycle(bom.productId, componentSkuIds, {\n tenantId,\n });\n if (path) throw new BomCycleError(bom.id || [...billIds][0] || '', path);\n }\n\n private resolveCached(\n skuId: string,\n cache: Map<string, Promise<ResolvedComponent>>,\n options: ResolveComponentOptions,\n ): Promise<ResolvedComponent> {\n let pending = cache.get(skuId);\n if (!pending) {\n pending = this.resolveComponent(skuId, options);\n cache.set(skuId, pending);\n }\n return pending;\n }\n}\n\n/** Convenience factory for {@link AssemblyService}. */\nexport function createAssemblyService(\n options: AssemblyServiceOptions,\n): Promise<AssemblyService> {\n return AssemblyService.create(options);\n}\n","/**\n * The shared step of every multi-level walk in {@link BomService}: read a\n * bill's lines, resolve each component, and decide whether to open a\n * sub-assembly's active bill. Explosion, planning, cost and labour all walk\n * the same way through this class, so they agree on what is opened.\n *\n * A walk is bounded three ways: by the caller's `levels`, by\n * {@link MAX_EXPLOSION_DEPTH} and {@link MAX_EXPLOSION_LINES}, and by the path\n * of products above the current line, which turns a cycle already present in\n * stored data into a {@link BomStructureCycleError} instead of a loop.\n *\n * @internal\n * @packageDocumentation\n */\n\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport {\n BomExplosionLimitError,\n type ExplosionLevels,\n type ExplosionPathEntry,\n MAX_EXPLOSION_DEPTH,\n MAX_EXPLOSION_LINES,\n} from '../types.js';\nimport {\n type AssemblyService,\n BomCycleError,\n type BomCyclePathEntry,\n type ResolvedComponent,\n} from './AssemblyService.js';\n\n/**\n * Thrown when a multi-level walk meets a product that already contains\n * itself in stored data (a loop saved before cycle refusal existed, or\n * written around it). Carries the loop as `path`, like the\n * {@link BomCycleError} it extends.\n */\nexport class BomStructureCycleError extends BomCycleError {\n override name = 'BomStructureCycleError';\n\n constructor(bomId: string, path: BomCyclePathEntry[]) {\n super(bomId, path);\n this.message = `The bill structure contains a cycle: ${path\n .map((entry) => entry.name)\n .join(' → ')}`;\n }\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * The deepest level a walk may open for `levels`. Throws for anything that is\n * not a whole number from 1 to {@link MAX_EXPLOSION_DEPTH} or `'all'`.\n */\nexport function maxLevelFor(levels: ExplosionLevels | undefined): number {\n if (levels === undefined) return 1;\n if (levels === 'all') return MAX_EXPLOSION_DEPTH;\n if (!Number.isInteger(levels) || levels < 1 || levels > MAX_EXPLOSION_DEPTH)\n throw new Error(\n `levels must be a whole number from 1 to ${MAX_EXPLOSION_DEPTH}, or 'all' (got ${String(levels)})`,\n );\n return levels;\n}\n\n/** `true` when the caller asked for a walk rather than the single-level form. */\nexport function isMultiLevel(levels: ExplosionLevels | undefined): boolean {\n return maxLevelFor(levels) > 1;\n}\n\n/** One walk from one top bill; caches what it reads for the walk's lifetime. */\nexport class StructureWalk {\n private readonly components = new Map<string, Promise<ResolvedComponent>>();\n private readonly bills = new Map<string, Promise<BomLine[]>>();\n private lineCount = 0;\n /** The deepest level whose lines the walk reads. */\n readonly maxLevel: number;\n\n constructor(\n private readonly assemblies: AssemblyService,\n /** The bill the walk starts from. */\n readonly top: BillOfMaterials,\n /** The caller's `levels`. */\n readonly levels: ExplosionLevels,\n ) {\n this.maxLevel = maxLevelFor(levels);\n }\n\n /**\n * Resolve a component SKU, reading assemblies' active bills in the top\n * bill's tenant so a shared assembly opens that tenant's bill.\n */\n resolve(skuId: string): Promise<ResolvedComponent> {\n let pending = this.components.get(skuId);\n if (!pending) {\n pending = this.assemblies.resolveComponent(skuId, {\n tenantId: this.top.tenantId ?? null,\n });\n this.components.set(skuId, pending);\n }\n return pending;\n }\n\n /**\n * A bill's lines in the top bill's tenant (and global lines), counted\n * against {@link MAX_EXPLOSION_LINES} every time the walk visits them (a\n * shared sub-assembly counts once per use).\n */\n async linesOf(bomId: string): Promise<BomLine[]> {\n let pending = this.bills.get(bomId);\n if (!pending) {\n // The top bill's structure only: its tenant's lines and global ones,\n // as `AssemblyService` reads a structure.\n const tenantId = this.top.tenantId ?? null;\n pending = readOwnAndGlobal(tenantId, () =>\n this.assemblies.lines.findByBom(bomId),\n ).then((lines) => lines.filter((line) => isOwnOrGlobal(line, tenantId)));\n this.bills.set(bomId, pending);\n }\n const lines = await pending;\n this.lineCount += lines.length;\n if (this.lineCount > MAX_EXPLOSION_LINES)\n throw new BomExplosionLimitError(this.top.id as string, 'lines');\n return lines;\n }\n\n /** The path entry for the top bill. */\n async root(): Promise<ExplosionPathEntry> {\n const productId = this.top.productId;\n let name = productId;\n if (UUID_PATTERN.test(productId)) {\n // Only a name the top bill's tenant may see.\n const owner = this.top.tenantId ?? null;\n const [product] = await readOwnAndGlobal(owner, () =>\n this.assemblies.products.list({ where: { id: productId }, limit: 1 }),\n );\n if (product?.name && isOwnOrGlobal(product, owner)) name = product.name;\n }\n return { bomId: this.top.id as string, productId, name };\n }\n\n /**\n * The bill to open below a line at `level`, or `null` when the component\n * is not a sub-assembly with an active bill or `levels` stops here.\n *\n * @throws {BomStructureCycleError} when the sub-assembly is already on\n * `path` (it would contain itself).\n * @throws {BomExplosionLimitError} when `levels: 'all'` would go deeper\n * than {@link MAX_EXPLOSION_DEPTH}.\n */\n open(\n component: ResolvedComponent,\n level: number,\n path: readonly ExplosionPathEntry[],\n ): ExplosionPathEntry | null {\n const bom = component.activeBom;\n const product = component.product;\n if (!component.buildable || !bom?.id || !product?.id) return null;\n const name = product.name || product.id;\n const at = path.findIndex((entry) => entry.productId === product.id);\n if (at >= 0)\n throw new BomStructureCycleError(this.top.id as string, [\n ...path\n .slice(at)\n .map((entry) => ({ productId: entry.productId, name: entry.name })),\n { productId: product.id, name },\n ]);\n if (level >= this.maxLevel) {\n if (this.levels === 'all')\n throw new BomExplosionLimitError(this.top.id as string, 'depth');\n return null;\n }\n return { bomId: bom.id, productId: product.id, name };\n }\n}\n","/**\n * BomService — cost rollup, requirements explosion, and \"can we make this?\"\n * stock-availability checks for a {@link BillOfMaterials}.\n *\n * The service is the sanctioned planning surface for BOM-driven workflows.\n * It never mutates stock — it answers questions:\n *\n * - \"How much does it cost to make one unit?\" via {@link computeMaterialCost}\n * - \"What do I need to buy / pull to make N units?\" via {@link explodeRequirements}\n * - \"Do I have enough on hand?\" via {@link canProduce}\n *\n * Every method reads one level of the bill by default. Pass `{ levels }` to\n * walk sub-assemblies too: a line whose component SKU belongs to an\n * `Assembly` with an active bill is opened, to the given depth or (`'all'`)\n * down to materials and bought items. {@link explode} returns that walk line\n * by line with the rolled-up totals, and {@link planRequirements} nets it\n * against stock: how much of each sub-assembly is available, how much is\n * short, and what building the shortfall takes.\n *\n * Construct via {@link BomService.create} — the static factory wires up\n * the BOM, BOM-line, and inventory collections so a single `db` (or a\n * pre-built {@link StockService}) drives everything.\n *\n * Material cost resolution is pluggable. Pass a `costResolver` callback to\n * supply unit costs from anywhere — `@happyvertical/smrt-products`\n * (`Material.costPerUnit`), a purchase-order rolling average, a vendor\n * price book, anything. When no resolver is supplied, costs default to\n * `0` and the per-line `costUnavailable` flag is set so UIs can surface\n * \"missing cost data\" rather than silently rolling up a $0 BOM.\n *\n * @packageDocumentation\n */\n\nimport { type DatabaseConfig, resolveDatabase } from '@happyvertical/smrt-core';\nimport {\n createStockService,\n type StockService,\n} from '@happyvertical/smrt-inventory';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { BomLineCollection } from '../collections/BomLineCollection.js';\nimport { OperationCollection } from '../collections/OperationCollection.js';\nimport { RoutingStepCollection } from '../collections/RoutingStepCollection.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport type {\n LabourEstimate,\n OperationRateResolver,\n RoutingStepEstimate,\n} from '../operation-types.js';\nimport { roundQuantity } from '../quantity.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport {\n type BomCostRollup,\n type BomLineCost,\n BomNotFoundError,\n type CanProduceResult,\n type ExplodedLine,\n type Explosion,\n type ExplosionOptions,\n type ExplosionPathEntry,\n type MaterialRequirement,\n type MaterialShortage,\n type PlannedLine,\n type RequirementsPlan,\n type SubAssemblyLabour,\n} from '../types.js';\nimport { AssemblyService, type ResolvedComponent } from './AssemblyService.js';\nimport { isMultiLevel, StructureWalk } from './structure-walk.js';\n\n/**\n * Callback signature for resolving the unit cost of a component SKU.\n *\n * Implementations should return the latest known cost in the BOM's\n * currency, or `null` (or `undefined`) when no cost is available. The\n * service treats unresolved costs as `0` and marks the affected line so\n * callers can warn the user.\n *\n * @example\n * ```typescript\n * import { MaterialCollection } from '@happyvertical/smrt-products/models';\n *\n * const materials = await MaterialCollection.create({ db });\n *\n * const bom = await BomService.create({\n * db,\n * costResolver: async (componentSkuId) => {\n * const sku = await skus.get(componentSkuId);\n * if (!sku?.productId) return null;\n * const material = await materials.get(sku.productId);\n * return material?.costPerUnit ?? null;\n * },\n * });\n * ```\n */\nexport type ComponentCostResolver = (\n componentSkuId: string,\n) => Promise<number | null | undefined> | number | null | undefined;\n\n/**\n * Options accepted by {@link BomService.create}.\n *\n * Either provide a `db` for the service to construct its own internal\n * collections + {@link StockService}, or pass a pre-built `stockService`\n * to share one across subsystems.\n */\nexport type BomServiceOptions = {\n /**\n * Optional resolver that returns the latest known unit cost for a\n * component SKU. See {@link ComponentCostResolver}. When omitted, all\n * costs default to `0` and per-line `costUnavailable` is set.\n */\n costResolver?: ComponentCostResolver;\n /**\n * Optional resolver that returns the hourly labour rate for an operation,\n * used by {@link BomService.computeLabourEstimate}. When omitted, the\n * estimate reports minutes only and flags every step `rateUnavailable`.\n */\n rateResolver?: OperationRateResolver;\n} & (\n | { db: DatabaseConfig; stockService?: StockService }\n | { stockService: StockService; db?: DatabaseConfig }\n);\n\n/**\n * BOM-driven planning service. See module documentation for the role\n * this plays in the manufacturing pipeline.\n *\n * @example\n * ```typescript\n * const bomService = await BomService.create({ db });\n * const rollup = await bomService.computeMaterialCost(bom.id!);\n * const requirements = await bomService.explodeRequirements(bom.id!, 100);\n * const check = await bomService.canProduce(bom.id!, 100);\n * ```\n */\nexport class BomService {\n private constructor(\n public readonly boms: BillOfMaterialsCollection,\n public readonly lines: BomLineCollection,\n public readonly stockService: StockService,\n private readonly costResolver: ComponentCostResolver | undefined,\n public readonly routingSteps: RoutingStepCollection,\n public readonly operations: OperationCollection,\n private readonly rateResolver: OperationRateResolver | undefined,\n /** Resolves components for the multi-level walks. */\n public readonly assemblies: AssemblyService,\n ) {}\n\n /** Factory — prefer {@link createBomService}. */\n static async create(options: BomServiceOptions): Promise<BomService> {\n const stockService =\n options.stockService ?? (await buildStockService(options));\n // Share the same db that the StockService uses so reads always hit\n // one connection / pool. StockService exposes its `db` as a public\n // field specifically so downstream services can compose against it\n // without reaching into Collection internals.\n // Resolve once: a config object such as `{ type: 'sqlite', url: ':memory:' }`\n // would otherwise yield a separate database per collection.\n const sharedDb = (await resolveDatabase(\n options.stockService ? (options.db ?? stockService.db) : stockService.db,\n )) as unknown as DatabaseConfig;\n const [boms, lines, routingSteps, operations, assemblies] =\n await Promise.all([\n BillOfMaterialsCollection.create({ db: sharedDb }),\n BomLineCollection.create({ db: sharedDb }),\n RoutingStepCollection.create({ db: sharedDb }),\n OperationCollection.create({ db: sharedDb }),\n AssemblyService.create({ db: sharedDb }),\n ]);\n return new BomService(\n boms,\n lines,\n stockService,\n options.costResolver,\n routingSteps,\n operations,\n options.rateResolver,\n assemblies,\n );\n }\n\n /**\n * Walk every {@link BomLine} for the given BOM, resolve each line's\n * component cost, apply waste, and return the rolled-up material cost\n * per produced unit along with a per-line breakdown.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist.\n *\n * Lines whose cost cannot be resolved contribute `0` to the total and\n * set `costUnavailable: true` on their breakdown row; the aggregate\n * `hasMissingCosts` flag mirrors this so callers can surface a UI\n * warning.\n *\n * With `{ levels }` above 1, a sub-assembly line whose bill the walk opens\n * is costed from that bill instead of the resolver: its `unitCost` is the\n * bill's rolled-up material cost per unit, its `components` hold that\n * bill's lines, and a missing cost anywhere below marks it and\n * `hasMissingCosts` (its known part still counts, so the total is a lower\n * bound). Other lines, including sub-assemblies with no active bill, use\n * the resolver as before.\n */\n async computeMaterialCost(\n bomId: string,\n options: ExplosionOptions = {},\n ): Promise<BomCostRollup> {\n if (isMultiLevel(options.levels))\n return this.computeMultiLevelCost(bomId, options);\n const bom = await this.requireBom(bomId);\n const lines = await this.linesOf(bom);\n const lineBreakdown: BomLineCost[] = [];\n let totalCost = 0;\n let hasMissingCosts = false;\n\n for (const line of lines) {\n const effectiveQty = line.effectiveQtyPerUnit();\n const resolved = await this.resolveCost(line.componentSkuId);\n const unitCost = resolved ?? 0;\n const costUnavailable = resolved === null || resolved === undefined;\n const lineCost = unitCost * effectiveQty;\n if (costUnavailable) {\n hasMissingCosts = true;\n } else {\n totalCost += lineCost;\n }\n\n lineBreakdown.push({\n componentSkuId: line.componentSkuId,\n qtyPerUnit: Number(line.qtyPerUnit ?? 0),\n wastePercent: Number(line.wastePercent ?? 0),\n effectiveQty,\n unitCost,\n lineCost,\n uom: line.uom,\n costUnavailable,\n });\n }\n\n return {\n bomId,\n totalCost,\n currency: bom.currency || 'USD',\n lineBreakdown,\n hasMissingCosts,\n };\n }\n\n /**\n * Roll up the estimated labour to build one unit from the bill's routing:\n * total minutes, plus cost when a `rateResolver` was supplied (hourly rate\n * times minutes, per step). A bill with no routing returns an empty\n * estimate with zero totals, so callers can add it to the material cost\n * unconditionally. Retired operations still count: the routing names them.\n *\n * With `{ levels }` above 1, every sub-assembly the walk opens adds its own\n * bill's routing, multiplied by the units of it one unit needs (waste\n * included); see {@link LabourEstimate.subAssemblies}.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist.\n */\n async computeLabourEstimate(\n bomId: string,\n options: ExplosionOptions = {},\n ): Promise<LabourEstimate> {\n if (isMultiLevel(options.levels))\n return this.computeMultiLevelLabour(bomId, options);\n return this.labourFor(await this.requireBom(bomId));\n }\n\n /** One bill's own routing estimate, per unit of its product. */\n private async labourFor(bom: BillOfMaterials): Promise<LabourEstimate> {\n const bomId = bom.id as string;\n // The bill's own tenant's routing and operations, and global ones: a\n // shared global sub-assembly keeps its routing under a tenant context.\n const owner = bom.tenantId ?? null;\n const routing = (\n await readOwnAndGlobal(owner, () => this.routingSteps.findByBom(bomId))\n ).filter((step) => isOwnOrGlobal(step, owner));\n const steps: RoutingStepEstimate[] = [];\n let totalMinutes = 0;\n let totalCost = 0;\n let hasMissingRates = false;\n\n for (const step of routing) {\n const [operation] = (\n await readOwnAndGlobal(owner, () =>\n this.operations.list({ where: { id: step.operationId }, limit: 1 }),\n )\n ).filter((row) => isOwnOrGlobal(row, owner));\n const minutes = Number(step.estimatedMinutes ?? 0);\n const rate = operation ? await this.resolveRate(operation) : null;\n const rateUnavailable = rate === null;\n const stepCost = rate === null ? 0 : (minutes / 60) * rate;\n totalMinutes += minutes;\n if (rateUnavailable) hasMissingRates = true;\n else totalCost += stepCost;\n steps.push({\n stepId: step.id as string,\n sequence: step.sequence,\n operationId: step.operationId,\n operationCode: operation?.code ?? '',\n operationName: operation?.name ?? '',\n estimatedMinutes: minutes,\n hourlyRate: rate ?? 0,\n stepCost,\n rateUnavailable,\n });\n }\n\n return {\n bomId,\n hasRouting: steps.length > 0,\n totalMinutes,\n totalCost,\n currency: bom.currency || 'USD',\n steps,\n hasMissingRates,\n };\n }\n\n /**\n * Return a \"shopping list\" of materials needed to produce `qty` units of\n * the parent product against the given BOM. Lines that reference the\n * same component SKU are summed so each `componentSkuId` appears once\n * in the result.\n *\n * Does NOT mutate stock — purely a planning helper. Use\n * {@link ProductionService.consumeMaterials} when you're ready to write\n * stock movements.\n *\n * With `{ levels }` above 1, sub-assemblies with an active bill are\n * replaced by what their bills need, waste compounding down the levels;\n * this is {@link explode}'s `totals`.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist; throws a\n * plain `Error` when `qty` is not a positive finite number.\n */\n async explodeRequirements(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<MaterialRequirement[]> {\n if (isMultiLevel(options.levels))\n return (await this.explode(bomId, qty, options)).totals;\n assertPositiveQty(qty, 'explodeRequirements');\n const lines = await this.linesOf(await this.requireBom(bomId));\n\n // Sum across duplicate component SKUs. Keep the first-seen `uom` per\n // SKU; if two lines disagree on `uom`, we surface the first one and\n // assume the caller noticed during BOM authoring.\n const aggregate = new Map<string, MaterialRequirement>();\n for (const line of lines) {\n const effectiveQty = line.effectiveQtyPerUnit() * qty;\n const existing = aggregate.get(line.componentSkuId);\n if (existing) {\n existing.totalQty += effectiveQty;\n } else {\n aggregate.set(line.componentSkuId, {\n componentSkuId: line.componentSkuId,\n totalQty: effectiveQty,\n uom: line.uom,\n });\n }\n }\n\n return Array.from(aggregate.values());\n }\n\n /**\n * Check whether the requirements for producing `qty` units against the\n * given BOM are currently satisfied by available stock. Returns\n * `{ ok: true, shortages: [] }` when every component has enough\n * `available` stock across all locations; `{ ok: false, shortages: [...] }`\n * with one entry per insufficient component otherwise.\n *\n * Available stock is summed across every location (the planning\n * question is \"do we have it at all?\"; the operational question of\n * \"where do we pull from?\" is left to the caller of\n * {@link ProductionService.consumeMaterials}).\n *\n * With `{ levels }` above 1 the check is {@link planRequirements}: a\n * sub-assembly that is short and has an active bill is opened for its\n * shortfall, and each shortage is reported on the line where it occurs,\n * with its `level` and `path`. Stock is allocated as the walk goes, so a\n * component used in two places is not counted twice.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist.\n */\n async canProduce(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<CanProduceResult> {\n if (isMultiLevel(options.levels)) {\n const plan = await this.planRequirements(bomId, qty, options);\n return plan.ok\n ? { ok: true, shortages: [] }\n : { ok: false, shortages: plan.shortages };\n }\n const requirements = await this.explodeRequirements(bomId, qty);\n\n // Fan the per-component availability queries out in parallel so a\n // BOM with N components costs O(1) round-trips of latency instead\n // of O(N). `totalForSku` is a single aggregate read per component\n // — independent across SKUs — so there's no ordering or\n // consistency concern from running them concurrently.\n const availabilities = await Promise.all(\n requirements.map(async (requirement) => ({\n requirement,\n available: await this.stockService.levels.totalForSku(\n requirement.componentSkuId,\n 'available',\n ),\n })),\n );\n\n const shortages: MaterialShortage[] = [];\n for (const { requirement, available } of availabilities) {\n if (available < requirement.totalQty) {\n shortages.push({\n componentSkuId: requirement.componentSkuId,\n requested: requirement.totalQty,\n available,\n });\n }\n }\n\n if (shortages.length === 0) {\n return { ok: true, shortages: [] };\n }\n return { ok: false, shortages };\n }\n\n /**\n * Walk the bill for `qty` units and return every line, depth first, with\n * its level, the path of bills above it and its quantity for the run, plus\n * the rolled-up `totals`. Gross requirements: stock is not read.\n *\n * `levels` (default `1`) says how far to open sub-assemblies; see\n * {@link ExplosionLevels}. Waste compounds: a sub-assembly line's quantity\n * includes its own waste, and its bill's lines are multiplied by it.\n *\n * Throws {@link BomNotFoundError}, `BomStructureCycleError` when stored\n * data already has a product containing itself, `BomExplosionLimitError`\n * past the depth or line limits, and a plain `Error` for a bad `qty` or\n * `levels`.\n */\n async explode(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<Explosion> {\n assertPositiveQty(qty, 'explode');\n const levels = options.levels ?? 1;\n const top = await this.requireBom(bomId);\n const walk = new StructureWalk(this.assemblies, top, levels);\n const lines: ExplodedLine[] = [];\n\n const visit = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n parentQty: number,\n ): Promise<void> => {\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const sub = walk.open(component, level, path);\n const exploded = explodedLine(line, component, level, path, parentQty);\n exploded.expanded = sub !== null;\n lines.push(exploded);\n if (sub)\n await visit(sub.bomId, level + 1, [...path, sub], exploded.totalQty);\n }\n };\n await visit(bomId, 1, [await walk.root()], qty);\n\n return {\n bomId,\n productId: top.productId,\n qty,\n levels,\n lines,\n totals: sumRequirements(lines.filter((line) => !line.expanded)),\n };\n }\n\n /**\n * Net the walk against stock: for each line, the available stock (all\n * locations) not yet allocated to an earlier line and how much is short.\n * A sub-assembly that is short and has an active bill is opened for the\n * shortfall only, so its lines say what building that shortfall takes;\n * one covered by stock is not opened. Lines that are short and cannot be\n * opened are the `shortages`, each at its own level.\n *\n * These are facts, not a decision: the application decides whether a\n * short sub-assembly is built or bought. Does not mutate stock.\n *\n * Throws as {@link explode} does.\n */\n async planRequirements(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<RequirementsPlan> {\n assertPositiveQty(qty, 'planRequirements');\n const levels = options.levels ?? 1;\n const top = await this.requireBom(bomId);\n const walk = new StructureWalk(this.assemblies, top, levels);\n const lines: PlannedLine[] = [];\n const shortages: MaterialShortage[] = [];\n const unallocated = new Map<string, Promise<number>>();\n const tenantId = top.tenantId ?? null;\n const availableFor = (skuId: string): Promise<number> => {\n let pending = unallocated.get(skuId);\n if (!pending) {\n pending = this.availableInTenant(skuId, tenantId);\n unallocated.set(skuId, pending);\n }\n return pending;\n };\n\n const visit = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n parentQty: number,\n ): Promise<void> => {\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const base = explodedLine(line, component, level, path, parentQty);\n const available = await availableFor(line.componentSkuId);\n const allocated = Math.min(available, base.totalQty);\n unallocated.set(\n line.componentSkuId,\n Promise.resolve(available - allocated),\n );\n // Rounded to the quantity precision: `2.2 * 100` against 220 in\n // stock is not short.\n const short = Math.max(0, roundQuantity(base.totalQty - allocated));\n const sub = short > 0 ? walk.open(component, level, path) : null;\n lines.push({ ...base, expanded: sub !== null, available, short });\n if (sub) {\n await visit(sub.bomId, level + 1, [...path, sub], short);\n } else if (short > 0) {\n shortages.push({\n componentSkuId: line.componentSkuId,\n requested: base.totalQty,\n available,\n level,\n path,\n uom: base.uom,\n buildable: component.buildable,\n });\n }\n }\n };\n await visit(bomId, 1, [await walk.root()], qty);\n\n return {\n bomId,\n productId: top.productId,\n qty,\n levels,\n lines,\n shortages,\n ok: shortages.length === 0,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Internal helpers\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Fetch a BOM by id or throw {@link BomNotFoundError}. Centralises the\n * \"missing BOM\" error path so callers don't have to repeat the check.\n */\n /**\n * A bill's lines: its own tenant's and global ones, whatever tenant\n * context is active (a context alone would hide a global bill's lines).\n */\n private async linesOf(bom: BillOfMaterials): Promise<BomLine[]> {\n const owner = bom.tenantId ?? null;\n return (\n await readOwnAndGlobal(owner, () =>\n this.lines.findByBom(bom.id as string),\n )\n ).filter((line) => isOwnOrGlobal(line, owner));\n }\n\n private async requireBom(bomId: string): Promise<BillOfMaterials> {\n if (!bomId) throw new BomNotFoundError(bomId);\n // Under a tenant context a global bill is hidden from `get`; read the\n // tenant's bills and global ones (another tenant's never).\n const tenantId = getCurrentTenant()?.tenantId;\n const bom = tenantId\n ? ((\n await readOwnAndGlobal(tenantId, () =>\n this.boms.list({ where: { id: bomId }, limit: 1 }),\n )\n ).find((row) => isOwnOrGlobal(row, tenantId)) ?? null)\n : await this.boms.get(bomId);\n if (!bom) throw new BomNotFoundError(bomId);\n return bom;\n }\n\n /**\n * Available stock of a SKU across locations, counting only the top bill's\n * tenant's rows and global ones, whatever tenant context is active (the\n * walk reads its structure the same way).\n */\n private async availableInTenant(\n skuId: string,\n tenantId: string | null,\n ): Promise<number> {\n const rows = await readOwnAndGlobal(tenantId, () =>\n this.stockService.levels.list({ where: { skuId, state: 'available' } }),\n );\n let total = 0;\n for (const row of rows)\n if (isOwnOrGlobal(row, tenantId)) total += Number(row.qty ?? 0);\n return Math.max(0, total);\n }\n\n /** {@link computeMaterialCost} with `levels` above 1. */\n private async computeMultiLevelCost(\n bomId: string,\n options: ExplosionOptions,\n ): Promise<BomCostRollup> {\n const top = await this.requireBom(bomId);\n const currency = top.currency || 'USD';\n const walk = new StructureWalk(this.assemblies, top, options.levels ?? 1);\n\n const rollUp = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n ): Promise<Omit<BomCostRollup, 'bomId' | 'currency'>> => {\n const lineBreakdown: BomLineCost[] = [];\n let totalCost = 0;\n let hasMissingCosts = false;\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const sub = walk.open(component, level, path);\n const effectiveQty = line.effectiveQtyPerUnit();\n const entry: BomLineCost = {\n componentSkuId: line.componentSkuId,\n qtyPerUnit: Number(line.qtyPerUnit ?? 0),\n wastePercent: Number(line.wastePercent ?? 0),\n effectiveQty,\n unitCost: 0,\n lineCost: 0,\n uom: line.uom,\n costUnavailable: false,\n };\n // A sub-assembly bill in another currency cannot be added to this\n // total; its line is priced through the resolver, as an unopened one.\n const sameCurrency =\n (component.activeBom?.currency || 'USD') === currency;\n if (sub && sameCurrency) {\n const inner = await rollUp(sub.bomId, level + 1, [...path, sub]);\n entry.unitCost = inner.totalCost;\n entry.lineCost = inner.totalCost * effectiveQty;\n entry.costUnavailable = inner.hasMissingCosts;\n entry.subBomId = sub.bomId;\n entry.components = inner.lineBreakdown;\n totalCost += entry.lineCost;\n } else {\n const resolved = await this.resolveCost(line.componentSkuId);\n entry.unitCost = resolved ?? 0;\n entry.lineCost = entry.unitCost * effectiveQty;\n entry.costUnavailable = resolved === null;\n if (!entry.costUnavailable) totalCost += entry.lineCost;\n }\n if (entry.costUnavailable) hasMissingCosts = true;\n lineBreakdown.push(entry);\n }\n return { totalCost, lineBreakdown, hasMissingCosts };\n };\n\n const rolled = await rollUp(bomId, 1, [await walk.root()]);\n return { bomId, currency, ...rolled };\n }\n\n /** {@link computeLabourEstimate} with `levels` above 1. */\n private async computeMultiLevelLabour(\n bomId: string,\n options: ExplosionOptions,\n ): Promise<LabourEstimate> {\n const top = await this.requireBom(bomId);\n const walk = new StructureWalk(this.assemblies, top, options.levels ?? 1);\n const own = await this.labourFor(top);\n const estimates = new Map<string, Promise<LabourEstimate>>();\n const subAssemblies: SubAssemblyLabour[] = [];\n\n const visit = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n units: number,\n ): Promise<void> => {\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const sub = walk.open(component, level, path);\n if (!sub || !component.activeBom) continue;\n const subBill = component.activeBom;\n let estimate = estimates.get(sub.bomId);\n if (!estimate) {\n estimate = this.labourFor(subBill);\n estimates.set(sub.bomId, estimate);\n }\n const unitsPerUnit = units * line.effectiveQtyPerUnit();\n subAssemblies.push({\n lineId: line.id as string,\n componentSkuId: line.componentSkuId,\n name: sub.name,\n level,\n path,\n unitsPerUnit,\n estimate: await estimate,\n });\n await visit(sub.bomId, level + 1, [...path, sub], unitsPerUnit);\n }\n };\n await visit(bomId, 1, [await walk.root()], 1);\n\n let totalMinutes = own.totalMinutes;\n let totalCost = own.totalCost;\n let hasMissingRates = own.hasMissingRates;\n for (const sub of subAssemblies) {\n totalMinutes += sub.unitsPerUnit * sub.estimate.totalMinutes;\n // Cost in another currency is not added; the total is then a lower\n // bound, as with a missing rate.\n if (sub.estimate.currency === own.currency) {\n totalCost += sub.unitsPerUnit * sub.estimate.totalCost;\n if (sub.estimate.hasMissingRates) hasMissingRates = true;\n } else if (sub.estimate.hasRouting) {\n hasMissingRates = true;\n }\n }\n return {\n ...own,\n hasRouting:\n own.hasRouting || subAssemblies.some((sub) => sub.estimate.hasRouting),\n totalMinutes,\n totalCost,\n hasMissingRates,\n subAssemblies,\n };\n }\n\n /** Run the {@link OperationRateResolver}; `null` when there is no rate. */\n private async resolveRate(\n operation: Parameters<OperationRateResolver>[0],\n ): Promise<number | null> {\n if (!this.rateResolver) return null;\n const value = await this.rateResolver(operation);\n if (value === null || value === undefined || !Number.isFinite(value))\n return null;\n return Number(value);\n }\n\n /**\n * Run the supplied {@link ComponentCostResolver} for the given component\n * SKU. Returns `null` when no resolver is registered or when the\n * resolver returns `null` / `undefined`.\n */\n private async resolveCost(componentSkuId: string): Promise<number | null> {\n if (!this.costResolver) return null;\n const value = await this.costResolver(componentSkuId);\n if (value === null || value === undefined) return null;\n if (!Number.isFinite(value)) return null;\n return Number(value);\n }\n}\n\n/**\n * One line of a walk before stock is considered: quantities for the run,\n * with the parent's quantity (which already carries every waste above it)\n * times this line's quantity per unit including its own waste.\n */\nfunction explodedLine(\n line: BomLine,\n component: ResolvedComponent,\n level: number,\n path: ExplosionPathEntry[],\n parentQty: number,\n): ExplodedLine {\n const effectiveQtyPerUnit = line.effectiveQtyPerUnit();\n return {\n lineId: line.id as string,\n bomId: line.bomId,\n level,\n path,\n componentSkuId: line.componentSkuId,\n kind: component.kind,\n name: component.product?.name ?? '',\n skuCode: component.sku?.code ?? '',\n qtyPerUnit: Number(line.qtyPerUnit ?? 0),\n wastePercent: Number(line.wastePercent ?? 0),\n effectiveQtyPerUnit,\n uom: line.uom,\n totalQty: parentQty * effectiveQtyPerUnit,\n buildable: component.buildable,\n subBomId: component.activeBom?.id ?? null,\n expanded: false,\n };\n}\n\n/**\n * Sum lines per component SKU, keeping the first unit seen (as the\n * single-level explosion does).\n */\nfunction sumRequirements(lines: ExplodedLine[]): MaterialRequirement[] {\n const totals = new Map<string, MaterialRequirement>();\n for (const line of lines) {\n const existing = totals.get(line.componentSkuId);\n if (existing) existing.totalQty += line.totalQty;\n else\n totals.set(line.componentSkuId, {\n componentSkuId: line.componentSkuId,\n totalQty: line.totalQty,\n uom: line.uom,\n });\n }\n return Array.from(totals.values());\n}\n\n/**\n * Reject zero / negative / non-finite quantities up front for service\n * methods that accept production-run quantities.\n */\nfunction assertPositiveQty(qty: number, op: string): void {\n if (!Number.isFinite(qty) || qty <= 0) {\n throw new Error(`${op}: qty must be a positive finite number (got ${qty})`);\n }\n}\n\n/**\n * Build a `StockService` from a `db` config in the options. Called only\n * when the caller did not pass a pre-built `stockService` — the option\n * union guarantees `db` is present in that case, but TypeScript can't\n * narrow the discriminated union from a `??` value check, so this helper\n * validates at runtime with a clear message.\n */\nasync function buildStockService(\n options: BomServiceOptions,\n): Promise<StockService> {\n if (!options.db) {\n throw new Error(\n 'BomService.create: either `db` or `stockService` is required',\n );\n }\n return createStockService({ db: options.db });\n}\n\n/**\n * Convenience factory. Returns a fully-initialized {@link BomService}.\n */\nexport async function createBomService(\n options: BomServiceOptions,\n): Promise<BomService> {\n return BomService.create(options);\n}\n\n// Re-export the BomLine type alias to ease typing in adjacent code that\n// wires `BomLine[]` payloads through this service.\nexport type { BomLine };\n","/**\n * ProductionService — the operational bridge from a production order to\n * actual stock-mutating consume/produce events.\n *\n * Production orders themselves live in `@happyvertical/smrt-commerce` as a\n * `Contract` STI subtype. This service does NOT modify those rows; it\n * simply reads the order's `productId`, looks up the active BOM, and\n * writes the right stock movements through {@link StockService}.\n *\n * Three methods:\n *\n * - {@link consumeMaterials} — for each BOM line, deduct\n * `qtyPerUnit * (1 + wastePercent / 100) * runQty` from `available` at\n * the production location. Stamps every movement with\n * `sourceType: 'ProductionOrder'` + the production order id so audit\n * queries can roll them up later. Atomic across BOM lines (one tx).\n * - {@link produceFinishedGoods} — receive `runQty` of the finished\n * product's SKU into `available` at the production location. The caller\n * supplies the SKU id (the production order references a *product*, not\n * a *SKU*; in practice the production-order-level \"what we are making\"\n * gets resolved to a specific SKU per variant on the line items).\n * - {@link runProduction} — consume materials AND receive finished\n * goods in one transaction. Use this when consume + produce should be\n * jointly atomic (e.g. a make-to-stock workflow where the factory step\n * is invisible). Calling `consumeMaterials` then `produceFinishedGoods`\n * separately is NOT jointly atomic — each opens its own transaction\n * and a failure between them can leave materials deducted with no\n * finished SKU receipt to balance it.\n *\n * ## Location convention (explicit-arg design)\n *\n * The original design considered carrying `originLocationId` on a per-\n * package mixin of `ProductionOrder`, but that adds a meta field to a\n * model owned by a sibling package. The cleaner design — and the one\n * shipped here — keeps the location explicit on the call:\n *\n * ```typescript\n * await production.consumeMaterials(order, { locationId, qty });\n * await production.produceFinishedGoods(order, { locationId, qty, finishedSkuId });\n * ```\n *\n * Callers that want a \"default factory\" convention can stash the\n * `locationId` on their own production-order helper or wrap this service\n * with a thin domain-level façade.\n *\n * @packageDocumentation\n */\n\nimport type { DatabaseConfig } from '@happyvertical/smrt-core';\nimport type {\n StockMovementReason,\n StockMutationOptions,\n StockService,\n} from '@happyvertical/smrt-inventory';\nimport { createStockService } from '@happyvertical/smrt-inventory';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { BomLineCollection } from '../collections/BomLineCollection.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport { BomNotFoundError, NoActiveBomForProductError } from '../types.js';\n\n/**\n * Minimal shape of a production order this service needs. We deliberately\n * accept this structural type rather than importing the `ProductionOrder`\n * class from `@happyvertical/smrt-commerce` — that would either pull a\n * dep on commerce into manufacturing or force commerce to extend a\n * manufacturing interface. Plain duck typing keeps the dependency arrow\n * pointing the way the architecture asks for (manufacturing builds on\n * inventory; commerce stays orthogonal).\n */\nexport interface ProductionOrderRef {\n /** Production order row id. Used for `sourceId` on every movement. */\n id?: string | null;\n /**\n * Plain string reference to the upstream product the order is asked to\n * produce. Used to look up the active BOM for the consume step.\n */\n productId?: string;\n /**\n * Optional explicit BOM id. When set, {@link consumeMaterials} uses this\n * row even if the product has a different `active` BOM — useful when an\n * order was kicked off against a specific revision.\n */\n bomId?: string;\n}\n\n/**\n * Per-call options for {@link ProductionService.consumeMaterials}.\n */\nexport interface ConsumeMaterialsOptions {\n /**\n * Inventory location id (warehouse / factory) where the materials are\n * pulled from. Required because the production order itself does not\n * carry a stocking location — see \"Location convention\" in the module\n * doc.\n */\n locationId: string;\n /**\n * Run quantity — how many finished units the BOM is being multiplied\n * by. Each BOM line's `effectiveQtyPerUnit()` is multiplied by this\n * number before being deducted from `available` stock.\n */\n qty: number;\n /**\n * Optional override of the reason code stamped on each\n * {@link StockMovement}. Defaults to `'production_consume'`.\n */\n reasonCode?: StockMovementReason;\n /**\n * Optional free-form note attached to every emitted movement.\n */\n note?: string;\n /**\n * Optional override of the `sourceType` stamped on each movement, with\n * the order's `id` as `sourceId`. Defaults to `'ProductionOrder'`;\n * `ProductionRunService` passes `'ProductionRunCompletion'`.\n */\n sourceType?: string;\n}\n\n/**\n * Per-call options for {@link ProductionService.produceFinishedGoods}.\n */\nexport interface ProduceFinishedGoodsOptions {\n /**\n * Inventory location id (warehouse / factory) where the finished goods\n * land. Required for the same reason as {@link ConsumeMaterialsOptions.locationId}.\n */\n locationId: string;\n /**\n * Quantity of finished goods produced. Always positive.\n */\n qty: number;\n /**\n * The SKU id of the finished product to receive into `available`. The\n * production order references a `productId` (a `Product` from\n * `@happyvertical/smrt-products`), but a product typically has multiple\n * SKUs (one per variant). The caller decides which concrete SKU each\n * line of the run produces.\n */\n finishedSkuId: string;\n /**\n * Optional override of the reason code stamped on the\n * {@link StockMovement}. Defaults to `'production_produce'`.\n */\n reasonCode?: StockMovementReason;\n /**\n * Optional free-form note attached to the emitted movement.\n */\n note?: string;\n /**\n * Optional override of the `sourceType` stamped on the movement. Defaults\n * to `'ProductionOrder'`; see {@link ConsumeMaterialsOptions.sourceType}.\n */\n sourceType?: string;\n}\n\n/**\n * Options accepted by {@link ProductionService.create}.\n *\n * Either pass a `db` for the service to construct its own\n * collections + {@link StockService}, or share a pre-built `stockService`\n * across subsystems.\n */\nexport type ProductionServiceOptions =\n | { db: DatabaseConfig; stockService?: StockService }\n | { stockService: StockService; db?: DatabaseConfig };\n\n/**\n * A single emitted stock movement, returned by\n * {@link ProductionService.consumeMaterials} so callers can audit / log\n * the impact of a production run without re-querying.\n */\nexport interface ConsumeResult {\n /** Component SKU id whose stock was deducted. */\n componentSkuId: string;\n /** Quantity deducted (already inflated for waste). */\n qty: number;\n /** Location the stock was deducted from. */\n locationId: string;\n}\n\n/**\n * Result of {@link ProductionService.produceFinishedGoods}.\n */\nexport interface ProduceResult {\n /** Finished SKU id whose stock increased. */\n finishedSkuId: string;\n /** Quantity received. */\n qty: number;\n /** Location the stock was received into. */\n locationId: string;\n}\n\n/**\n * Result of {@link ProductionService.runProduction} — the consume + produce\n * pair, run inside one transaction.\n */\nexport interface RunProductionResult {\n consumed: ConsumeResult[];\n produced: ProduceResult;\n}\n\n/**\n * Operational bridge between a production order and the inventory ledger.\n */\nexport class ProductionService {\n private constructor(\n /**\n * BOM collection bound to the OUTER (non-tx) database. Used for\n * pre-flight reads in `prepareConsume()` — those run before the\n * stock-service transaction opens, by design (read-then-tx keeps\n * the transaction window narrow and rollback only covers actual\n * mutations). DO NOT add BOM writes that depend on the stock-tx\n * rolling them back together — this collection is not tx-scoped\n * and writes here would commit independently. If joint BOM-plus-\n * stock mutations are ever needed, build tx-scoped BOM collections\n * inside the `stockService.withTransaction` callback (mirror the\n * pattern in StockService.withTransaction itself).\n */\n public readonly boms: BillOfMaterialsCollection,\n /**\n * BomLine collection bound to the OUTER database. Same caveat as\n * {@link boms} — pre-flight reads only.\n */\n public readonly lines: BomLineCollection,\n public readonly stockService: StockService,\n ) {}\n\n /** Factory — prefer {@link createProductionService}. */\n static async create(\n options: ProductionServiceOptions,\n ): Promise<ProductionService> {\n const stockService =\n options.stockService ?? (await buildStockService(options));\n // Share the StockService's db so production + stock writes ride one\n // connection/pool. StockService.db is exposed publicly for this case.\n const sharedDb = options.db ?? stockService.db;\n const [boms, lines] = await Promise.all([\n BillOfMaterialsCollection.create({ db: sharedDb }),\n BomLineCollection.create({ db: sharedDb }),\n ]);\n return new ProductionService(boms, lines, stockService);\n }\n\n /**\n * Walk every BOM line for the production order's BOM and deduct\n * `effectiveQtyPerUnit() * qty` from `available` at `locationId`. Each\n * deduction goes through {@link StockService.adjust} with `sourceType:\n * 'ProductionOrder'` and `sourceId: order.id`. Returns the list of\n * movements emitted so callers can log / surface them.\n *\n * **Atomic across lines**: all per-line deductions and their audit\n * movements run inside a single `stockService.withTransaction(...)`\n * scope. An `InsufficientStockError` on line N+1 rolls back lines 1..N\n * so production-order posting never leaves materials half-consumed.\n * Pre-flight with {@link BomService.canProduce} when you'd rather know\n * upfront than discover the shortfall mid-run.\n *\n * Throws {@link NoActiveBomForProductError} if no BOM id is supplied\n * on the order *and* no active BOM exists for the order's `productId`.\n * Throws {@link BomNotFoundError} if `order.bomId` is supplied but\n * doesn't resolve. Throws plain `Error` on missing `locationId`,\n * non-positive `qty`, or when both `order.bomId` and `order.productId`\n * are absent (a programmer error — neither input identifies a BOM).\n * Re-throws `InsufficientStockError` from `StockService.adjust` if a\n * line would drive `available` below zero.\n */\n async consumeMaterials(\n order: ProductionOrderRef,\n options: ConsumeMaterialsOptions,\n ): Promise<ConsumeResult[]> {\n const ctx = await this.prepareConsume(order, options);\n return this.stockService.withTransaction(async (tx) =>\n this.consumeMaterialsWith(tx, ctx),\n );\n }\n\n /**\n * Receive `qty` of the finished SKU into `available` at `locationId`.\n * Goes through {@link StockService.receive} with `sourceType:\n * 'ProductionOrder'` and `sourceId: order.id`. Returns a single\n * {@link ProduceResult} describing what landed.\n *\n * Throws plain `Error` on missing required arguments or non-positive\n * `qty`.\n */\n async produceFinishedGoods(\n order: ProductionOrderRef,\n options: ProduceFinishedGoodsOptions,\n ): Promise<ProduceResult> {\n const ctx = prepareProduce(order, options);\n return this.stockService.withTransaction(async (tx) =>\n this.produceFinishedGoodsWith(tx, ctx),\n );\n }\n\n /**\n * Atomically consume materials AND receive finished goods for one\n * production run.\n *\n * Both calls share a single `stockService.withTransaction(...)` scope:\n *\n * - Materials are deducted line-by-line via {@link StockService.adjust}.\n * - Finished goods are received via {@link StockService.receive}.\n * - If ANY step throws — a BOM-line shortage, the produce-leg adapter\n * failing, a tenancy mismatch surfaced by an interceptor — every\n * write in the transaction rolls back. The materials are restored\n * and the finished SKU's `available` row is unchanged. Callers\n * never observe \"materials deducted but finished goods missing\".\n *\n * Use this when consume + produce should be one indivisible event —\n * e.g. a make-to-stock workflow posting `production_order:completed`\n * where the shop floor step is invisible to the ledger.\n *\n * The two-call form (call {@link consumeMaterials} then {@link\n * produceFinishedGoods} separately) is still appropriate when the\n * factory step is a real wall-clock gap that other observers need to\n * see — work-in-progress dashboards, partial-run reporting, etc.\n *\n * Throws the same errors as the individual methods: missing args\n * (plain `Error`), unresolvable BOM ({@link BomNotFoundError} /\n * {@link NoActiveBomForProductError}), or `InsufficientStockError`\n * if a line would drive `available` below zero.\n */\n async runProduction(\n order: ProductionOrderRef,\n options: {\n consume: ConsumeMaterialsOptions;\n produce: ProduceFinishedGoodsOptions;\n },\n ): Promise<RunProductionResult> {\n const consumeCtx = await this.prepareConsume(order, options.consume);\n const produceCtx = prepareProduce(order, options.produce);\n return this.stockService.withTransaction(async (tx) => {\n const consumed = await this.consumeMaterialsWith(tx, consumeCtx);\n const produced = await this.produceFinishedGoodsWith(tx, produceCtx);\n return { consumed, produced };\n });\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Internal helpers\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Validate consume args, resolve the BOM + lines, and pre-compute the\n * mutation attribution. Anything that can throw without a tx open\n * happens here so the transaction we open later doesn't have to roll\n * back over a validation miss.\n */\n private async prepareConsume(\n order: ProductionOrderRef,\n options: ConsumeMaterialsOptions,\n ): Promise<ConsumeContext> {\n assertLocationId(options.locationId, 'consumeMaterials');\n assertPositiveQty(options.qty, 'consumeMaterials');\n const orderId = order.id ?? '';\n if (!orderId) {\n throw new Error(\n 'consumeMaterials: order.id is required for source attribution',\n );\n }\n const bom = await this.resolveBom(order);\n // The bill's own structure: its tenant's lines and global ones, never a\n // line another tenant wrote against this bill id (an unscoped read\n // would return those too).\n const owner = bom.tenantId ?? null;\n const lines = (\n await readOwnAndGlobal(owner, () => this.lines.findByBom(bom.id!))\n ).filter((line) => isOwnOrGlobal(line, owner));\n return {\n orderId,\n lines,\n runQty: options.qty,\n locationId: options.locationId,\n mutationOptions: {\n sourceType: options.sourceType ?? 'ProductionOrder',\n sourceId: orderId,\n reasonCode: options.reasonCode ?? 'production_consume',\n note: options.note,\n },\n };\n }\n\n /**\n * Execute the consume leg against a caller-supplied tx-bound\n * {@link StockService}. Public entry points (`consumeMaterials`,\n * `runProduction`) open the tx and call through here. Each per-line\n * `tx.adjust(...)` shares the same transaction so a shortfall on\n * line N+1 rolls back lines 1..N.\n */\n private async consumeMaterialsWith(\n tx: StockService,\n ctx: ConsumeContext,\n ): Promise<ConsumeResult[]> {\n const emitted: ConsumeResult[] = [];\n for (const line of ctx.lines) {\n const qty = line.effectiveQtyPerUnit() * ctx.runQty;\n if (qty <= 0) continue;\n await tx.adjust(\n line.componentSkuId,\n ctx.locationId,\n -qty,\n ctx.mutationOptions,\n );\n emitted.push({\n componentSkuId: line.componentSkuId,\n qty,\n locationId: ctx.locationId,\n });\n }\n return emitted;\n }\n\n /**\n * Execute the produce leg against a caller-supplied tx-bound\n * {@link StockService}. Public entry points (`produceFinishedGoods`,\n * `runProduction`) open the tx and call through here.\n */\n private async produceFinishedGoodsWith(\n tx: StockService,\n ctx: ProduceContext,\n ): Promise<ProduceResult> {\n await tx.receive(\n ctx.finishedSkuId,\n ctx.locationId,\n ctx.qty,\n ctx.mutationOptions,\n );\n return {\n finishedSkuId: ctx.finishedSkuId,\n qty: ctx.qty,\n locationId: ctx.locationId,\n };\n }\n\n /**\n * Resolve the BOM for a production order. Order of preference:\n *\n * 1. Explicit `order.bomId` if supplied — the caller pinned a specific\n * revision; we honor that pin or fail. We deliberately do NOT fall\n * back to the active BOM when `order.bomId` is stale or mistyped,\n * because silently switching recipes would have the production run\n * consume materials against a different BOM than the order locked.\n * 2. The active BOM for `order.productId`.\n *\n * Throws {@link BomNotFoundError} when an explicit `order.bomId` is\n * supplied but doesn't resolve to a row.\n *\n * Throws {@link NoActiveBomForProductError} when no `bomId` was supplied\n * and the product has no active BOM.\n *\n * Throws plain `Error` when neither `order.bomId` nor `order.productId`\n * is supplied — there's nothing to resolve against, so we surface that\n * as a programmer error with a distinct message rather than overload\n * `NoActiveBomForProductError` with an empty product id.\n */\n private async resolveBom(\n order: ProductionOrderRef,\n ): Promise<BillOfMaterials> {\n if (order.bomId) {\n const bomId = order.bomId;\n // Under a tenant context a global bill is hidden from `get`; read the\n // tenant's bills and global ones instead (another tenant's never).\n const tenantId = getCurrentTenant()?.tenantId;\n const bom = tenantId\n ? ((\n await readOwnAndGlobal(tenantId, () =>\n this.boms.list({ where: { id: bomId }, limit: 1 }),\n )\n ).find((row) => isOwnOrGlobal(row, tenantId)) ?? null)\n : await this.boms.get(bomId);\n if (!bom) throw new BomNotFoundError(bomId);\n return bom;\n }\n const productId = order.productId ?? '';\n if (!productId) {\n // The order carries neither an explicit `bomId` nor a `productId`,\n // so there is nothing for us to resolve against. Surface that as a\n // distinct, actionable error rather than constructing\n // `NoActiveBomForProductError('')` — the empty-string message\n // (\"No active Bill of Materials found for product: ...\") buries\n // the actual misuse (missing input field) under a SQL-flavoured\n // \"no row found\" error that doesn't reflect what went wrong.\n throw new Error(\n 'consumeMaterials: order.productId is required when order.bomId is not supplied',\n );\n }\n const active = await this.boms.findActiveForProduct(productId);\n if (!active) throw new NoActiveBomForProductError(productId);\n return active;\n }\n}\n\n/**\n * Pre-computed consume work that an open transaction can apply without\n * any further validation. Built by {@link ProductionService.prepareConsume}.\n */\ninterface ConsumeContext {\n orderId: string;\n lines: BomLine[];\n runQty: number;\n locationId: string;\n mutationOptions: StockMutationOptions;\n}\n\n/**\n * Pre-computed produce work that an open transaction can apply without\n * any further validation. Built by {@link prepareProduce}.\n */\ninterface ProduceContext {\n orderId: string;\n finishedSkuId: string;\n qty: number;\n locationId: string;\n mutationOptions: StockMutationOptions;\n}\n\n/**\n * Validate produce args and pre-compute the mutation attribution.\n * Mirrors {@link ProductionService.prepareConsume}; it's a free function\n * because the produce leg doesn't need access to any collection state.\n */\nfunction prepareProduce(\n order: ProductionOrderRef,\n options: ProduceFinishedGoodsOptions,\n): ProduceContext {\n assertLocationId(options.locationId, 'produceFinishedGoods');\n assertPositiveQty(options.qty, 'produceFinishedGoods');\n if (!options.finishedSkuId) {\n throw new Error(\n 'produceFinishedGoods: finishedSkuId is required (the production order references a productId; the caller picks which SKU is being produced)',\n );\n }\n const orderId = order.id ?? '';\n if (!orderId) {\n throw new Error(\n 'produceFinishedGoods: order.id is required for source attribution',\n );\n }\n return {\n orderId,\n finishedSkuId: options.finishedSkuId,\n qty: options.qty,\n locationId: options.locationId,\n mutationOptions: {\n sourceType: options.sourceType ?? 'ProductionOrder',\n sourceId: orderId,\n reasonCode: options.reasonCode ?? 'production_produce',\n note: options.note,\n },\n };\n}\n\nfunction assertPositiveQty(qty: number, op: string): void {\n if (!Number.isFinite(qty) || qty <= 0) {\n throw new Error(`${op}: qty must be a positive finite number (got ${qty})`);\n }\n}\n\nfunction assertLocationId(locationId: string, op: string): void {\n if (!locationId || typeof locationId !== 'string') {\n throw new Error(`${op}: locationId is required`);\n }\n}\n\n/**\n * Build a `StockService` from a `db` config in the options. Called only\n * when the caller did not pass a pre-built `stockService` — the option\n * union guarantees `db` is present in that case, but TypeScript can't\n * narrow the discriminated union from a `??` value check, so this helper\n * validates at runtime with a clear message.\n */\nasync function buildStockService(\n options: ProductionServiceOptions,\n): Promise<StockService> {\n if (!options.db) {\n throw new Error(\n 'ProductionService.create: either `db` or `stockService` is required',\n );\n }\n return createStockService({ db: options.db });\n}\n\n/**\n * Convenience factory. Returns a fully-initialized {@link ProductionService}.\n */\nexport async function createProductionService(\n options: ProductionServiceOptions,\n): Promise<ProductionService> {\n return ProductionService.create(options);\n}\n\n// Re-export the BomLine type so callers don't have to reach into the\n// models barrel just to type service results.\nexport type { BomLine };\n","/**\n * Opt-in DispatchBus subscribers that bridge upstream production-order\n * signals to {@link ProductionService}.\n *\n * **Off by default.** The package never auto-subscribes — consumer apps\n * call {@link installManufacturingDispatchHandlers} from their `smrt.ts`\n * wiring once they have decided they want a `production_order:posted`\n * emit to consume materials and produce finished goods automatically.\n *\n * Two paired toggles let consumers wire only the legs they want:\n *\n * - `installProductionPosted` (default `true`) — on `production_order:posted`,\n * call {@link ProductionService.consumeMaterials} once.\n * - `installProductionCompleted` (default `false`) — on `production_order:completed`,\n * call {@link ProductionService.produceFinishedGoods} once. Off by default\n * because not every shop emits a separate \"completed\" event; lots of\n * workflows expect consume + produce to happen atomically when the\n * order is posted. Set both flags to `true` to get the split lifecycle.\n *\n * @packageDocumentation\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type {\n DatabaseConfig,\n DispatchBus,\n DispatchHandler,\n DispatchMetadata,\n} from '@happyvertical/smrt-core';\nimport type { StockService } from '@happyvertical/smrt-inventory';\nimport {\n createProductionService,\n type ProductionService,\n} from './ProductionService.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Shape of a `production_order:posted` payload this handler expects.\n *\n * The producer (typically the application's smrt.ts wiring once a\n * `ProductionOrder` row transitions to the posted state) packs one\n * payload per posted order. The handler consumes materials for every\n * unit declared by `qty`, attributed to `('ProductionOrder', productionOrderId)`\n * on each emitted {@link StockMovement}.\n */\nexport interface ProductionOrderPostedPayload {\n /** Production order row id. Used for `sourceId` on every movement. */\n productionOrderId: string;\n /**\n * Plain string reference to the upstream product the order is asked to\n * produce. Used to resolve the active BOM at consume time.\n */\n productId: string;\n /**\n * Inventory location id (factory / warehouse) where materials are\n * pulled from.\n */\n locationId: string;\n /**\n * Run quantity. Each BOM line's effective qty is multiplied by this\n * before being deducted from `available`.\n */\n qty: number;\n /**\n * Optional explicit BOM id, e.g. when the order locked itself to a\n * specific BOM revision when it was created. Falls back to the active\n * BOM for `productId` if omitted.\n */\n bomId?: string;\n /**\n * Optional finished SKU id. When provided AND the\n * `installProductionPosted` handler is wired to also produce in one\n * shot (which only happens when `installProductionCompleted` is false\n * and `producedOnPosted` is true), this is the SKU to receive into\n * `available`.\n */\n finishedSkuId?: string;\n}\n\n/**\n * Shape of a `production_order:completed` payload this handler expects.\n *\n * Typically emitted when the factory signs off the run and finished\n * goods are physically available to ship. Only used when callers opt in\n * via `installProductionCompleted`.\n */\nexport interface ProductionOrderCompletedPayload {\n /** Production order row id. Used for `sourceId` on the receipt movement. */\n productionOrderId: string;\n /**\n * SKU id of the finished product to receive into `available`. Required.\n */\n finishedSkuId: string;\n /** Inventory location id where the finished goods land. */\n locationId: string;\n /** Quantity received. */\n qty: number;\n}\n\n/**\n * Options accepted by {@link installManufacturingDispatchHandlers}.\n *\n * Provide either a pre-built {@link ProductionService} (when sharing\n * across subsystems) or a `db` / `stockService` for the helper to\n * construct one on first use.\n */\nexport type InstallManufacturingDispatchHandlersOptions = {\n /** Bus to subscribe on. Typically the app-wide DispatchBus. */\n dispatchBus: DispatchBus;\n /**\n * When `true` (default), install the `production_order:posted` handler.\n */\n installProductionPosted?: boolean;\n /**\n * When `true` (default `false`), install the\n * `production_order:completed` handler. Off by default because many\n * workflows want consume+produce to happen on `posted`; opt in to the\n * split lifecycle when your shop emits a separate completion event.\n */\n installProductionCompleted?: boolean;\n /**\n * When `true` (default `false`) AND `installProductionCompleted` is\n * false, the `production_order:posted` handler also calls\n * {@link ProductionService.produceFinishedGoods} after consume —\n * handy for \"make-to-stock instantly\" workflows where the factory\n * step is invisible.\n *\n * Ignored when `installProductionCompleted` is `true` (in that case\n * production is the completed-handler's job).\n */\n producedOnPosted?: boolean;\n} & (\n | {\n productionService: ProductionService;\n db?: DatabaseConfig;\n stockService?: StockService;\n }\n | {\n stockService: StockService;\n db?: DatabaseConfig;\n productionService?: undefined;\n }\n | {\n db: DatabaseConfig;\n stockService?: undefined;\n productionService?: undefined;\n }\n);\n\n/**\n * Result handle returned by {@link installManufacturingDispatchHandlers}.\n * Call `dispose()` to detach the subscribers (mainly useful in tests and\n * on graceful shutdown).\n */\nexport interface InstalledManufacturingDispatchHandlers {\n /** Resolved ProductionService — useful for follow-up writes in the same scope. */\n productionService: ProductionService;\n /** Detach every installed subscriber. Idempotent. */\n dispose(): void;\n}\n\n/**\n * Subscribe production-order handlers to the given DispatchBus.\n *\n * @example\n * ```typescript\n * import { createDispatchBus } from '@happyvertical/smrt-core';\n * import { installManufacturingDispatchHandlers } from '@happyvertical/smrt-manufacturing';\n *\n * const bus = await createDispatchBus({ db });\n * const handlers = await installManufacturingDispatchHandlers({\n * dispatchBus: bus,\n * db,\n * producedOnPosted: true, // consume + produce in one shot\n * });\n * ```\n */\nexport async function installManufacturingDispatchHandlers(\n options: InstallManufacturingDispatchHandlersOptions,\n): Promise<InstalledManufacturingDispatchHandlers> {\n const {\n dispatchBus,\n installProductionPosted = true,\n installProductionCompleted = false,\n producedOnPosted = false,\n } = options;\n\n const productionService =\n options.productionService ?? (await buildProductionService(options));\n\n const installed: Array<{ pattern: string; handler: DispatchHandler }> = [];\n\n if (installProductionPosted) {\n const shouldProduceOnPosted =\n producedOnPosted && !installProductionCompleted;\n const handler: DispatchHandler = async (payload, metadata) => {\n await handleProductionPosted(\n productionService,\n payload as ProductionOrderPostedPayload,\n metadata,\n shouldProduceOnPosted,\n );\n };\n dispatchBus.on('production_order:posted', handler);\n installed.push({ pattern: 'production_order:posted', handler });\n }\n\n if (installProductionCompleted) {\n const handler: DispatchHandler = async (payload, metadata) => {\n await handleProductionCompleted(\n productionService,\n payload as ProductionOrderCompletedPayload,\n metadata,\n );\n };\n dispatchBus.on('production_order:completed', handler);\n installed.push({ pattern: 'production_order:completed', handler });\n }\n\n return {\n productionService,\n dispose() {\n for (const entry of installed) {\n dispatchBus.off(entry.pattern, entry.handler);\n }\n installed.length = 0;\n },\n };\n}\n\nasync function handleProductionPosted(\n productionService: ProductionService,\n payload: ProductionOrderPostedPayload | null | undefined,\n metadata: DispatchMetadata,\n shouldProduce: boolean,\n): Promise<void> {\n // Mirror the inventory-handler observability + strictness pattern:\n // producer contract drift goes silent without this warn (the\n // dispatch bus would just see a quick return and never surface\n // anything), and a non-string `productionOrderId` (e.g. a numeric\n // primary key coming from a Postgres source) would otherwise land\n // as a non-string `sourceId` on every emitted StockMovement —\n // breaking the audit-trail-by-source-id pattern.\n const postedReason = malformedProductionPayloadReason(payload);\n if (postedReason || !payload) {\n warnMalformedPayload(\n 'production_order:posted',\n payload,\n metadata,\n postedReason ?? 'payload is null/undefined',\n );\n return;\n }\n\n // If `producedOnPosted` is enabled, the produce leg is required — not\n // optional. Validate `finishedSkuId` before consuming materials so we\n // don't leave inventory depleted with no corresponding receipt. (Without\n // this guard the consume leg would land, the produce leg would silently\n // skip due to the falsy `finishedSkuId`, and the audit log would carry\n // production_consume movements but no matching production_produce.)\n if (shouldProduce && !payload.finishedSkuId) {\n throw new Error(\n `production_order:posted handler: producedOnPosted is enabled but ` +\n `payload.finishedSkuId is missing (productionOrderId=${payload.productionOrderId}). ` +\n `Either supply finishedSkuId on the event, or disable producedOnPosted ` +\n `and emit production_order:completed separately.`,\n );\n }\n\n const order = {\n id: payload.productionOrderId,\n productId: payload.productId,\n bomId: payload.bomId,\n };\n const consumeNote = metadata.source\n ? `auto-consume via ${metadata.source}`\n : undefined;\n\n // When producedOnPosted is on, route through runProduction so consume\n // and produce share a single transaction. The two calls below would\n // otherwise open separate transactions, and a process crash or adapter\n // error between them would leave materials deducted with no\n // finished-goods receipt to balance the audit ledger.\n if (shouldProduce && payload.finishedSkuId) {\n const produceNote = metadata.source\n ? `auto-produce via ${metadata.source}`\n : undefined;\n await productionService.runProduction(order, {\n consume: {\n locationId: payload.locationId,\n qty: payload.qty,\n note: consumeNote,\n },\n produce: {\n locationId: payload.locationId,\n qty: payload.qty,\n finishedSkuId: payload.finishedSkuId,\n note: produceNote,\n },\n });\n return;\n }\n\n // Consume-only path — the produce leg lives on the separate\n // `production_order:completed` event (or is not modeled at all in\n // this workflow). consumeMaterials is itself atomic across BOM lines.\n await productionService.consumeMaterials(order, {\n locationId: payload.locationId,\n qty: payload.qty,\n note: consumeNote,\n });\n}\n\nasync function handleProductionCompleted(\n productionService: ProductionService,\n payload: ProductionOrderCompletedPayload | null | undefined,\n metadata: DispatchMetadata,\n): Promise<void> {\n const completedReason = malformedProductionPayloadReason(payload);\n if (completedReason || !payload) {\n warnMalformedPayload(\n 'production_order:completed',\n payload,\n metadata,\n completedReason ?? 'payload is null/undefined',\n );\n return;\n }\n await productionService.produceFinishedGoods(\n { id: payload.productionOrderId },\n {\n locationId: payload.locationId,\n qty: payload.qty,\n finishedSkuId: payload.finishedSkuId,\n note: metadata.source ? `auto-produce via ${metadata.source}` : undefined,\n },\n );\n}\n\n/**\n * Determine whether a production-order payload (either\n * `:posted` or `:completed`) is structurally unusable and return a\n * concrete reason naming the specific field that failed validation.\n * Returns `null` when the top-level shape is acceptable. Mirrors the\n * inventory package's per-signal helpers so the log message names the\n * actual failure instead of a hardcoded generic.\n */\nfunction malformedProductionPayloadReason(\n payload: { productionOrderId?: unknown } | null | undefined,\n): string | null {\n if (!payload || typeof payload !== 'object') {\n return 'payload is null/undefined or not an object';\n }\n if (\n !payload.productionOrderId ||\n typeof payload.productionOrderId !== 'string'\n ) {\n return 'missing or non-string `productionOrderId` (required for audit-trail source attribution)';\n }\n return null;\n}\n\n/**\n * Surface dispatch payloads that don't match the documented shape.\n * Mirrors the inventory package's `warnMalformedPayload` so a\n * misshapen `production_order:*` event leaves a trace (signal name,\n * source attribution, specific reason, payload-key summary) instead\n * of vanishing into the dispatch bus's silent-success path. Without\n * this, producer drift only surfaces as \"stock didn't move\" /\n * \"audit log empty\" bugs a long way from the handler.\n */\nfunction warnMalformedPayload(\n signal: string,\n payload: unknown,\n metadata: DispatchMetadata,\n reason: string,\n): void {\n logger.warn(\n `[@happyvertical/smrt-manufacturing] dispatch handler ignored a ${signal} ` +\n `event with a malformed payload (${reason}). ` +\n `Source: ${metadata.source ?? '<unknown>'}; payload keys: ` +\n `${payload && typeof payload === 'object' ? Object.keys(payload).join(',') : typeof payload}`,\n );\n}\n\n/**\n * Build a `ProductionService` for the dispatch installer when no\n * pre-built service was supplied. The discriminated option union\n * guarantees one of `stockService` / `db` is set in that branch, but\n * TypeScript can't narrow from a `??` value check on a sibling property,\n * so we validate at runtime with a clear error message.\n */\nasync function buildProductionService(\n options: InstallManufacturingDispatchHandlersOptions,\n): Promise<ProductionService> {\n if (options.stockService) {\n // Pass `db` through when supplied alongside `stockService` so the\n // resulting ProductionService points its BOM collections at the\n // caller-supplied manufacturing database. Dropping `db` here would\n // make the BOM collections fall back to `stockService.db`, which\n // is the *inventory* connection in deployments where the two\n // packages target different databases. ProductionService.create\n // already accepts both fields together; the discriminated option\n // union just doesn't make the combined shape obvious.\n return createProductionService({\n stockService: options.stockService,\n ...(options.db ? { db: options.db } : {}),\n });\n }\n if (!options.db) {\n throw new Error(\n 'installManufacturingDispatchHandlers: one of `productionService`, `stockService`, or `db` is required',\n );\n }\n return createProductionService({ db: options.db });\n}\n","/**\n * OperationService — maintain the managed list of {@link Operation} rows:\n * define, rename, change category or required qualification, retire and\n * reinstate. Operations are never deleted.\n *\n * @packageDocumentation\n */\n\nimport type { DatabaseConfig } from '@happyvertical/smrt-core';\nimport { OperationCollection } from '../collections/OperationCollection.js';\nimport type { Operation } from '../models/Operation.js';\nimport {\n type DefineOperationInput,\n DuplicateOperationCodeError,\n InvalidOperationInputError,\n OperationNotFoundError,\n} from '../operation-types.js';\n\n/** Options accepted by {@link OperationService.create}. */\nexport interface OperationServiceOptions {\n db: DatabaseConfig;\n}\n\nexport class OperationService {\n private constructor(public readonly operations: OperationCollection) {}\n\n /** Factory — prefer {@link createOperationService}. */\n static async create(\n options: OperationServiceOptions,\n ): Promise<OperationService> {\n return new OperationService(\n await OperationCollection.create({ db: options.db }),\n );\n }\n\n /**\n * Add an operation. The code is trimmed and must be unique per tenant;\n * throws {@link DuplicateOperationCodeError} when it is taken (retired\n * operations keep their code).\n */\n async define(input: DefineOperationInput): Promise<Operation> {\n const code = input.code?.trim() ?? '';\n const name = input.name?.trim() ?? '';\n if (!code)\n throw new InvalidOperationInputError('Operation code is required.');\n if (!name)\n throw new InvalidOperationInputError('Operation name is required.');\n if (await this.operations.findByCode(code))\n throw new DuplicateOperationCodeError(code);\n try {\n // `create()` persists. A concurrent definer of the same code must fail\n // rather than overwrite, hence insert-only.\n return await this.operations.create({\n code,\n name,\n category: input.category?.trim() ?? '',\n requiredQualificationId: input.requiredQualificationId?.trim() || null,\n isActive: true,\n _insertOnly: true,\n });\n } catch (error) {\n if (await this.operations.findByCode(code))\n throw new DuplicateOperationCodeError(code);\n throw error;\n }\n }\n\n /** Fetch an operation or throw {@link OperationNotFoundError}. */\n async get(id: string): Promise<Operation> {\n const operation = id ? await this.operations.get(id) : null;\n if (!operation) throw new OperationNotFoundError(id);\n return operation;\n }\n\n /** List operations; retired ones only with `includeRetired`. */\n async list(options: { includeRetired?: boolean } = {}): Promise<Operation[]> {\n return this.operations.listOperations(options);\n }\n\n /** Change the display name. The code does not change. */\n async rename(id: string, name: string): Promise<Operation> {\n const next = name?.trim() ?? '';\n if (!next)\n throw new InvalidOperationInputError('Operation name is required.');\n const operation = await this.get(id);\n operation.name = next;\n await operation.save();\n return operation;\n }\n\n /** Change category and/or required qualification; an empty string clears. */\n async update(\n id: string,\n changes: { category?: string; requiredQualificationId?: string },\n ): Promise<Operation> {\n const operation = await this.get(id);\n if (changes.category !== undefined)\n operation.category = changes.category.trim();\n if (changes.requiredQualificationId !== undefined)\n operation.requiredQualificationId =\n changes.requiredQualificationId.trim() || null;\n await operation.save();\n return operation;\n }\n\n /** Retire: stays on history and routings, leaves pickers. Idempotent. */\n async retire(id: string): Promise<Operation> {\n return this.setActive(id, false);\n }\n\n /** Reinstate a retired operation. Idempotent. */\n async reinstate(id: string): Promise<Operation> {\n return this.setActive(id, true);\n }\n\n private async setActive(id: string, isActive: boolean): Promise<Operation> {\n const operation = await this.get(id);\n if (operation.isActive !== isActive) {\n operation.isActive = isActive;\n await operation.save();\n }\n return operation;\n }\n}\n\n/** Convenience factory. */\nexport async function createOperationService(\n options: OperationServiceOptions,\n): Promise<OperationService> {\n return OperationService.create(options);\n}\n","/**\n * ProductionRunService — create production runs and report work on them as\n * it happens: \"25 to build, 12 done\".\n *\n * Every write runs in one database transaction that first saves the run\n * row. That save is revision-guarded, so it takes the row's lock and a\n * concurrent writer's save fails with a revision conflict; the loser's whole\n * transaction is retried against the committed run. Two people reporting\n * completions at once therefore both count, and a run never goes past its\n * target.\n *\n * Recording a completion moves no stock unless the caller asks. With\n * `consume` and/or `produce`, it uses the {@link ProductionService} paths\n * inside the same transaction as the completion, so a stock shortfall rolls\n * the completion back too.\n *\n * @packageDocumentation\n */\n\nimport { type DatabaseConfig, resolveDatabase } from '@happyvertical/smrt-core';\nimport {\n createStockService,\n type StockService,\n} from '@happyvertical/smrt-inventory';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { ProductionRunCollection } from '../collections/ProductionRunCollection.js';\nimport { ProductionRunCompletionCollection } from '../collections/ProductionRunCompletionCollection.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { ProductionRun } from '../models/ProductionRun.js';\nimport type { ProductionRunCompletion } from '../models/ProductionRunCompletion.js';\nimport {\n type CreateProductionRunInput,\n InvalidProductionRunInputError,\n ProductionRunNotFoundError,\n ProductionRunOverCompletionError,\n ProductionRunStateError,\n type RecordCompletionInput,\n type RecordCompletionResult,\n} from '../production-run-types.js';\nimport {\n fromMillionths,\n MAX_QUANTITY,\n roundQuantity,\n toMillionths,\n} from '../quantity.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport { BomNotFoundError, NoActiveBomForProductError } from '../types.js';\nimport {\n type ConsumeResult,\n type ProduceResult,\n ProductionService,\n} from './ProductionService.js';\n\n/**\n * Options accepted by {@link ProductionRunService.create}. With a\n * `stockService`, its database is used for everything, so a completion and\n * its stock movements share one transaction.\n */\nexport type ProductionRunServiceOptions =\n | { db: DatabaseConfig; stockService?: undefined }\n | { stockService: StockService; db?: undefined };\n\ntype TransactionalDb = {\n transaction?: <T>(work: (txDb: unknown) => Promise<T>) => Promise<T>;\n};\n\n/** Collections bound to one open transaction. */\ninterface RunTransaction {\n stock: StockService;\n runs: ProductionRunCollection;\n completions: ProductionRunCompletionCollection;\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Statuses in which work can still be reported or the run changed. */\nconst OPEN_STATUSES = new Set(['planned', 'in_progress']);\n\n/**\n * Creates runs and records their progress. Construct with\n * {@link ProductionRunService.create} or {@link createProductionRunService}.\n *\n * @example\n * ```typescript\n * const runs = await ProductionRunService.create({ db });\n * const run = await runs.createRun({ productId: frame.id, targetQty: 25 });\n * await runs.recordCompletion(run.id!, { qty: 12 });\n * // run: 25 to build, 12 done, in_progress\n * ```\n */\nexport class ProductionRunService {\n private constructor(\n /** The stock service whose database every collection here shares. */\n public readonly stockService: StockService,\n /** Production runs (reads; writes go through this service). */\n public readonly runs: ProductionRunCollection,\n /** Completions (reads; writes go through this service). */\n public readonly completions: ProductionRunCompletionCollection,\n /** Bills, to pin a run's bill. */\n public readonly boms: BillOfMaterialsCollection,\n ) {}\n\n /** Factory — prefer {@link createProductionRunService}. */\n static async create(\n options: ProductionRunServiceOptions,\n ): Promise<ProductionRunService> {\n let stockService = options.stockService;\n if (!stockService) {\n if (!options.db)\n throw new Error(\n 'ProductionRunService.create: either `db` or `stockService` is required',\n );\n // Resolve once so the stock service and every collection share one\n // handle: a config such as `:memory:` would otherwise open a database\n // per collection.\n const resolved = (await resolveDatabase(\n options.db,\n )) as unknown as DatabaseConfig;\n stockService = await createStockService({ db: resolved });\n }\n const db = stockService.db;\n // Recording a completion is atomic only inside a transaction; refuse an\n // adapter that cannot open one rather than writing step by step.\n if (typeof (db as unknown as TransactionalDb).transaction !== 'function')\n throw new Error(\n 'ProductionRunService requires a database adapter with transaction() support.',\n );\n const [runs, completions, boms] = await Promise.all([\n ProductionRunCollection.create({ db }),\n ProductionRunCompletionCollection.create({ db }),\n BillOfMaterialsCollection.create({ db }),\n ]);\n return new ProductionRunService(stockService, runs, completions, boms);\n }\n\n /**\n * Create a `planned` run of `targetQty` units, pinned to `bomId`, or to the\n * active bill of `productId`.\n *\n * @throws {InvalidProductionRunInputError} for a target that is not a\n * positive number, or when neither `bomId` nor `productId` is given.\n * @throws {BomNotFoundError} when `bomId` does not exist.\n * @throws {NoActiveBomForProductError} when `productId` has no active bill.\n */\n async createRun(input: CreateProductionRunInput): Promise<ProductionRun> {\n const targetQty = positive(input.targetQty, 'targetQty');\n // Pinned to the active tenant's bills and global ones; without a tenant\n // context, global bills only. Another tenant's bill is never usable.\n const tenantId = getCurrentTenant()?.tenantId ?? null;\n let bom: BillOfMaterials | null = null;\n if (input.bomId) {\n const bomId = input.bomId;\n const found = UUID_PATTERN.test(bomId)\n ? await readOwnAndGlobal(tenantId, () =>\n this.boms.list({ where: { id: bomId }, limit: 1 }),\n )\n : [];\n bom = found.find((row) => isOwnOrGlobal(row, tenantId)) ?? null;\n if (!bom?.id) throw new BomNotFoundError(bomId);\n } else if (input.productId) {\n const productId = input.productId;\n // The tenant's own active bill first, then a global one.\n const active = (\n await readOwnAndGlobal(tenantId, () =>\n this.boms.list({\n where: { productId, status: 'active' },\n orderBy: 'version DESC',\n }),\n )\n ).filter((candidate) => isOwnOrGlobal(candidate, tenantId));\n bom =\n active.find((candidate) => (candidate.tenantId ?? null) === tenantId) ??\n active[0] ??\n null;\n if (!bom?.id) throw new NoActiveBomForProductError(input.productId);\n } else {\n throw new InvalidProductionRunInputError(\n 'A production run needs a bomId or a productId.',\n );\n }\n // `create()` persists; no second save. The run belongs to its bill's\n // tenant (or the active one, for a global bill).\n return this.runs.create({\n tenantId: bom.tenantId ?? tenantId,\n bomId: bom.id as string,\n targetQty,\n completedQty: 0,\n status: 'planned',\n });\n }\n\n /** The run, or `null` when no run has this id. */\n async get(runId: string): Promise<ProductionRun | null> {\n if (!UUID_PATTERN.test(runId)) return null;\n return this.runs.get({ id: runId });\n }\n\n /** A run's completions in the order they happened. */\n async listCompletions(runId: string): Promise<ProductionRunCompletion[]> {\n if (!UUID_PATTERN.test(runId)) return [];\n return this.completions.findByRun(runId);\n }\n\n /** Mark a `planned` run `in_progress`. */\n async start(runId: string): Promise<ProductionRun> {\n return this.change(runId, 'start', (run) => {\n if (run.status !== 'planned')\n throw new ProductionRunStateError(runId, run.status, 'start');\n run.status = 'in_progress';\n });\n }\n\n /** Mark an open run `done`, short of its target if work stopped early. */\n async finish(runId: string): Promise<ProductionRun> {\n return this.change(runId, 'finish', (run) => {\n run.status = 'done';\n });\n }\n\n /** Cancel an open run. Completions already reported stay. */\n async cancel(runId: string): Promise<ProductionRun> {\n return this.change(runId, 'cancel', (run) => {\n run.status = 'cancelled';\n });\n }\n\n /**\n * Change an open run's target. It may not go below what is already done;\n * setting it to exactly that finishes the run.\n *\n * @throws {InvalidProductionRunInputError} for a target that is not a\n * positive number or is below the completed quantity.\n */\n async setTarget(runId: string, targetQty: number): Promise<ProductionRun> {\n const target = positive(targetQty, 'targetQty');\n return this.change(runId, 'retarget', (run) => {\n // Whole millionths: exact, see quantity.ts.\n const completed = toMillionths(Number(run.completedQty));\n const goal = toMillionths(target);\n if (goal < completed)\n throw new InvalidProductionRunInputError(\n `The target cannot be below the ${fromMillionths(completed)} already done.`,\n );\n run.targetQty = target;\n if (completed > 0 && completed >= goal) run.status = 'done';\n });\n }\n\n /**\n * Report `qty` finished units on an open run: records a dated completion,\n * adds it to `completedQty`, moves a `planned` run to `in_progress`, and\n * marks the run `done` when the target is reached. With `consume` and/or\n * `produce`, also writes the stock movements through\n * {@link ProductionService}, stamped `sourceType: 'ProductionRunCompletion'`\n * with the completion id. Everything runs in one transaction.\n *\n * Consumption takes the run's bill's own lines for `qty` units: a\n * sub-assembly on the bill is taken from stock as a component. Building\n * that sub-assembly is its own run.\n *\n * @throws {ProductionRunNotFoundError}\n * @throws {ProductionRunStateError} when the run is `done` or `cancelled`.\n * @throws {ProductionRunOverCompletionError} when `qty` is more than the\n * run has left.\n * @throws {InvalidProductionRunInputError} for a bad `qty` or date.\n * @throws `InsufficientStockError` from inventory when consuming would\n * drive stock below zero; nothing is recorded then.\n */\n async recordCompletion(\n runId: string,\n input: RecordCompletionInput,\n ): Promise<RecordCompletionResult> {\n const qty = positive(input.qty, 'qty');\n const completedAt =\n input.completedAt === undefined\n ? new Date()\n : new Date(input.completedAt);\n if (Number.isNaN(completedAt.getTime()))\n throw new InvalidProductionRunInputError(\n 'completedAt is not a valid date.',\n );\n\n return this.inTransaction(async (tx) => {\n const run = await this.load(tx, runId);\n if (!OPEN_STATUSES.has(run.status))\n throw new ProductionRunStateError(runId, run.status, 'complete');\n // Whole millionths, so every report counts exactly (quantity.ts).\n const target = toMillionths(Number(run.targetQty));\n const done = toMillionths(Number(run.completedQty));\n const reported = toMillionths(qty);\n const remaining = Math.max(0, target - done);\n if (reported > remaining)\n throw new ProductionRunOverCompletionError(\n runId,\n qty,\n fromMillionths(remaining),\n );\n const total = done + reported;\n run.completedQty = fromMillionths(total);\n run.status = total >= target ? 'done' : 'in_progress';\n // Revision-guarded: a concurrent report fails here and is retried.\n await run.save();\n // `create()` persists; no second save.\n const completion = await tx.completions.create({\n tenantId: run.tenantId ?? null,\n runId,\n qty,\n completedAt,\n });\n\n let consumed: ConsumeResult[] = [];\n let produced: ProduceResult | null = null;\n // Stock reads and writes in the run's tenant, whatever the caller's\n // context: the bill's lines and the levels are that tenant's.\n const inRunTenant = <T>(work: () => Promise<T>): Promise<T> =>\n run.tenantId ? withTenant({ tenantId: run.tenantId }, work) : work();\n if (input.consume || input.produce) {\n // Bound to the open transaction: its own withTransaction joins it.\n const production = await ProductionService.create({\n stockService: tx.stock,\n });\n const ref = { id: completion.id, bomId: run.bomId };\n const sourceType = 'ProductionRunCompletion';\n const consume = input.consume && {\n ...input.consume,\n qty,\n sourceType,\n };\n const produce = input.produce && {\n ...input.produce,\n qty,\n sourceType,\n };\n if (consume && produce) {\n ({ consumed, produced } = await inRunTenant(() =>\n production.runProduction(ref, { consume, produce }),\n ));\n } else if (consume) {\n consumed = await inRunTenant(() =>\n production.consumeMaterials(ref, consume),\n );\n } else if (produce) {\n produced = await inRunTenant(() =>\n production.produceFinishedGoods(ref, produce),\n );\n }\n }\n return { run, completion, consumed, produced };\n });\n }\n\n /** Apply `apply` to an open run and save it, in one transaction. */\n private change(\n runId: string,\n action: string,\n apply: (run: ProductionRun) => void,\n ): Promise<ProductionRun> {\n return this.inTransaction(async (tx) => {\n const run = await this.load(tx, runId);\n if (!OPEN_STATUSES.has(run.status))\n throw new ProductionRunStateError(runId, run.status, action);\n apply(run);\n await run.save();\n return run;\n });\n }\n\n private async load(\n tx: RunTransaction,\n runId: string,\n ): Promise<ProductionRun> {\n const run = UUID_PATTERN.test(runId)\n ? await tx.runs.get({ id: runId })\n : null;\n if (!run) throw new ProductionRunNotFoundError(runId);\n return run;\n }\n\n /**\n * Run `work` in one transaction on collections bound to it, rerunning the\n * whole transaction when it lost a revision race on the run row.\n */\n private inTransaction<T>(\n work: (tx: RunTransaction) => Promise<T>,\n ): Promise<T> {\n return withRevisionRetry(() =>\n this.stockService.withTransaction(async (stock) => {\n const db = stock.db;\n const [runs, completions] = await Promise.all([\n ProductionRunCollection.create({ db }),\n ProductionRunCompletionCollection.create({ db }),\n ]);\n return work({ stock, runs, completions });\n }),\n );\n }\n}\n\n/** Retries allowed for one write that keeps losing the run row's revision race. */\nconst MAX_REVISION_ATTEMPTS = 10;\n\nasync function withRevisionRetry<T>(run: () => Promise<T>): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n try {\n return await run();\n } catch (error) {\n const code = (error as { code?: string } | null)?.code;\n if (\n code !== 'RUNTIME_REVISION_CONFLICT' ||\n attempt >= MAX_REVISION_ATTEMPTS\n )\n throw error;\n }\n }\n}\n\n/**\n * `value` as a quantity: finite, rounded to the quantity precision, greater\n * than zero after rounding, and no more than {@link MAX_QUANTITY}.\n */\nfunction positive(value: number, field: string): number {\n const raw = Number(value);\n const number = Number.isFinite(raw) ? roundQuantity(raw) : raw;\n if (!Number.isFinite(number) || number <= 0)\n throw new InvalidProductionRunInputError(\n `${field} must be a number greater than zero (got ${String(value)}).`,\n );\n if (number > MAX_QUANTITY)\n throw new InvalidProductionRunInputError(\n `${field} must be at most ${MAX_QUANTITY} (got ${String(value)}).`,\n );\n return number;\n}\n\n/** Convenience factory for {@link ProductionRunService}. */\nexport function createProductionRunService(\n options: ProductionRunServiceOptions,\n): Promise<ProductionRunService> {\n return ProductionRunService.create(options);\n}\n","/**\n * RoutingService — read and replace the routing of a bill of materials.\n *\n * A routing is the ordered list of {@link RoutingStep} rows for one bill.\n * It is optional; a bill with none is valid. `replaceRouting` swaps the whole\n * list in one transaction, so reordering, inserting and removing steps are one\n * operation and a bill never shows a half-written routing.\n *\n * @packageDocumentation\n */\n\nimport { type DatabaseConfig, resolveDatabase } from '@happyvertical/smrt-core';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { OperationCollection } from '../collections/OperationCollection.js';\nimport { RoutingStepCollection } from '../collections/RoutingStepCollection.js';\nimport type { RoutingStep } from '../models/RoutingStep.js';\nimport {\n InvalidOperationInputError,\n OperationNotFoundError,\n OperationRetiredError,\n type RoutingStepInput,\n} from '../operation-types.js';\nimport { BomNotFoundError } from '../types.js';\n\n/** Options accepted by {@link RoutingService.create}. */\nexport interface RoutingServiceOptions {\n db: DatabaseConfig;\n}\n\ntype TransactionalDb = {\n transaction?: <T>(work: (txDb: unknown) => Promise<T>) => Promise<T>;\n};\n\nexport class RoutingService {\n private constructor(\n private readonly db: DatabaseConfig,\n public readonly boms: BillOfMaterialsCollection,\n public readonly operations: OperationCollection,\n public readonly steps: RoutingStepCollection,\n ) {}\n\n /** Factory — prefer {@link createRoutingService}. */\n static async create(options: RoutingServiceOptions): Promise<RoutingService> {\n const db = (await resolveDatabase(options.db)) as unknown as DatabaseConfig;\n const [boms, operations, steps] = await Promise.all([\n BillOfMaterialsCollection.create({ db }),\n OperationCollection.create({ db }),\n RoutingStepCollection.create({ db }),\n ]);\n return new RoutingService(db, boms, operations, steps);\n }\n\n /** The routing of a bill in step order; empty when it has none. */\n async list(bomId: string): Promise<RoutingStep[]> {\n return this.steps.findByBom(bomId);\n }\n\n /**\n * Check `inputs` against the routing about to be replaced: durations,\n * operations, and that a retired operation is not added beyond the\n * occurrences `current` already has.\n */\n private async validate(\n current: readonly RoutingStep[],\n inputs: readonly RoutingStepInput[],\n ): Promise<void> {\n // Each occurrence already on the routing may be kept once.\n const retained = new Map<string, number>();\n for (const step of current)\n retained.set(step.operationId, (retained.get(step.operationId) ?? 0) + 1);\n for (const input of inputs) {\n const minutes = Number(input.estimatedMinutes);\n if (!Number.isFinite(minutes) || minutes < 0)\n throw new InvalidOperationInputError(\n 'Estimated minutes must be zero or more.',\n );\n const operation = input.operationId\n ? await this.operations.get(input.operationId)\n : null;\n if (!operation) throw new OperationNotFoundError(input.operationId);\n const left = retained.get(operation.id as string) ?? 0;\n if (left > 0) retained.set(operation.id as string, left - 1);\n else if (!operation.isActive)\n throw new OperationRetiredError(operation.id as string);\n }\n }\n\n /**\n * Replace the bill's routing with `inputs`, numbered 1..n in the order given.\n * An empty list removes the routing.\n *\n * Throws {@link BomNotFoundError}, {@link OperationNotFoundError},\n * {@link OperationRetiredError} (a retired operation may stay on a routing\n * that already has it but cannot be newly added), or\n * {@link InvalidOperationInputError} for a negative or non-finite duration.\n */\n async replaceRouting(\n bomId: string,\n inputs: readonly RoutingStepInput[],\n ): Promise<RoutingStep[]> {\n if (!bomId || !(await this.boms.get(bomId)))\n throw new BomNotFoundError(bomId);\n\n const write = async (\n steps: RoutingStepCollection,\n boms: BillOfMaterialsCollection,\n ) => {\n // Serialize replacements of one bill: saving the bill row takes its row\n // lock for the rest of the transaction, so a concurrent replacement\n // waits instead of interleaving. The waiter's revision check then fails;\n // `withRevisionRetry` reruns it against the committed routing.\n const bom = await boms.get(bomId);\n if (!bom) throw new BomNotFoundError(bomId);\n await bom.save();\n // Validate under the lock, against the routing this attempt will replace.\n const current = await steps.findByBom(bomId);\n await this.validate(current, inputs);\n for (const step of current) await step.delete();\n const saved: RoutingStep[] = [];\n for (const [index, input] of inputs.entries()) {\n // `create()` persists; no second save.\n saved.push(\n await steps.create({\n bomId,\n operationId: input.operationId,\n sequence: index + 1,\n estimatedMinutes: Number(input.estimatedMinutes),\n notes: input.notes?.trim() ?? '',\n }),\n );\n }\n return saved;\n };\n\n const underlying = this.db as unknown as TransactionalDb;\n // Delete-and-reinsert is only safe atomically; refuse adapters without it.\n if (typeof underlying.transaction !== 'function')\n throw new Error(\n 'RoutingService requires a database adapter with transaction() support.',\n );\n return withRevisionRetry(\n () =>\n underlying.transaction?.(async (txDb) => {\n const db = txDb as DatabaseConfig;\n return write(\n await RoutingStepCollection.create({ db }),\n await BillOfMaterialsCollection.create({ db }),\n );\n }) as Promise<RoutingStep[]>,\n );\n }\n}\n\n/**\n * Rerun a whole routing transaction when it lost the bill's revision race to a\n * concurrent replacement; the rerun reads the routing the winner committed.\n */\nasync function withRevisionRetry<T>(run: () => Promise<T>): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n try {\n return await run();\n } catch (error) {\n const code = (error as { code?: string } | null)?.code;\n if (code !== 'RUNTIME_REVISION_CONFLICT' || attempt >= 5) throw error;\n }\n }\n}\n\n/** Convenience factory. */\nexport async function createRoutingService(\n options: RoutingServiceOptions,\n): Promise<RoutingService> {\n return RoutingService.create(options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAOA,eAAe,wBAAwB,KAAK,MAAM,075CAAkrlD,CAAC;;;;;;;;;;;;;;;;;;ACuC9tlD,IAAM,YAAN,cAAwB,WAAW;CAGxC,WAA0B;CAQ1B,OAAe;CAIf,OAAe;;;;;CAMf,WAAmB;;;;;CAMnB,WAAoB;CAYpB,0BAAyC;CAEzC,YAAY,UAA4B,CAAC,GAAG;EAC1C,MAAM,OAAO;EACb,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,OAAO,QAAQ;EACpD,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,OAAO,QAAQ;EACpD,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,4BAA4B,KAAA,GACtC,KAAK,0BAA0B,QAAQ;CAC3C;;CAGA,MAAe,SAAwB;EACrC,MAAM,IAAI,MACR,aAAa,KAAK,QAAQ,KAAK,GAAE,uCACnC;CACF;AACF;AA1D4C,gBAA/B,WAAsC,mBAAkB,mCAAA;AAGnE,kBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GAFjB,UAGX,WAAA,YAAA,CAAA;AAQA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAVd,UAWX,WAAA,QAAA,CAAA;AAIA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAdd,UAeX,WAAA,QAAA,CAAA;AAwBA,kBAAA,CAHC,gBAAgB,qDAAqD,EACpE,UAAU,KACZ,CAAC,CAAA,GAtCU,UAuCX,WAAA,2BAAA,CAAA;AAvCW,YAAN,kBAAA,CAXN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAEX,iBAAiB,CAAC,QAAQ,WAAW;CAGrC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;AAClC,CAAC,CAAA,GACY,SAAA;;;;;;;;;;;;;;;;;;ACmBN,IAAM,WAAN,cAAuB,QAAQ;CAC3B,cAA2B,YAAY;CAQhD,yBAAiC;CAQjC,qBAAoC;CAOpC,gBAAwB;CAExB,YAAY,UAA2B,CAAC,GAAG;EACzC,MAAM,OAAO;EACb,IAAI,QAAQ,2BAA2B,KAAA,GACrC,KAAK,yBAAyB,QAAQ;EACxC,IAAI,QAAQ,uBAAuB,KAAA,GACjC,KAAK,qBAAqB,QAAQ;EACpC,IAAI,QAAQ,kBAAkB,KAAA,GAC5B,KAAK,gBAAgB,QAAQ;CACjC;;;;;CAMA,MAAyB,qBAAoC;EAC3D,MAAM,MAAM,mBAAmB;EAC/B,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,UAAU,GAC9C,MAAM,gBAAgB,aACpB,0BACA,SACA,yCACF;CAEJ;AACF;AAnDwC,gBAA3B,UAAkC,mBAAkB,mCAAA;AAS/D,kBAAA,CADC,MAAM,CAAA,GARI,SASX,WAAA,0BAAA,CAAA;AAQA,kBAAA,CADC,WAAW,WAAW;CAAE,UAAU;CAAM,UAAU;AAAW,CAAC,CAAA,GAhBpD,SAiBX,WAAA,sBAAA,CAAA;AAOA,kBAAA,CADC,MAAM,CAAA,GAvBI,SAwBX,WAAA,iBAAA,CAAA;AAxBW,WAAN,kBAAA,CAVN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,KAAK,EACH,SAAS;EAAC;EAAQ;EAAO;EAAU;CAAQ,EAC7C;CACA,KAAK,EACH,SAAS,CAAC,QAAQ,KAAK,EACzB;CACA,KAAK,EAAE,cAAc,KAAK;AAC5B,CAAC,CAAA,GACY,QAAA;;;ACpDN,IAAM,qBAAN,cAAiC,kBAAkB;CACxD,OAAyB,aAAa;AACxC;;;;;;;;;;;;;;;;;;ACqCO,IAAM,kBAAN,cAA8B,WAAW;CAG9C,WAA0B;CAS1B,YAAoB;CASpB,UAAkB;;;;;;CAOlB,gBAA6B;CAU7B,SAAoB;;;;;CAMpB,QAAgB;;;;;;CAOhB,WAAmB;CAEnB,YAAY,UAAkC,CAAC,GAAG;EAChD,MAAM,OAAO;EACb,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;EAC9D,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,kBAAkB,KAAA,GAC5B,KAAK,gBACH,QAAQ,kBAAkB,OACtB,OACA,QAAQ,yBAAyB,OAC/B,QAAQ,gBACR,IAAI,KAAK,QAAQ,aAAa;EAExC,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;EACxD,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;EACtD,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;CAC9D;;;;;;;;CASA,MAAyB,qBAAoC;EAC3D,MAAM,MAAM,mBAAmB;EAC/B,IAAI,KAAK,WAAW,UAAU;EAE9B,MAAM,EAAE,oBAAoB,MAAA,QAAA,QAAA,CAAA,CAAA,WAAA,uBAAA;EAE5B,OAAM,MADgB,gBAAgB,OAAO,EAAE,IAAI,KAAK,GAAG,CAAC,EAAA,CAC9C,kBAAkB,IAAI;CACtC;AACF;AAtFkD,gBAArC,iBAA4C,mBAAkB,mCAAA;AAGzE,kBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GAFjB,gBAGX,WAAA,YAAA,CAAA;AASA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAXd,gBAYX,WAAA,aAAA,CAAA;AASA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GApBd,gBAqBX,WAAA,WAAA,CAAA;AAiBA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GArCd,gBAsCX,WAAA,UAAA,CAAA;AAtCW,kBAAN,kBAAA,CAVN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAGX,iBAAiB;EAAC;EAAc;EAAW;CAAW;CACtD,KAAK,EAAE,SAAS;EAAC;EAAQ;EAAO;EAAU;CAAQ,EAAE;CACpD,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK;AACP,CAAC,CAAA,GACY,eAAA;;;AC1CN,IAAM,4BAAN,cAAwC,eAAgC;CAC7E,OAAgB,aAAa;;;;;;CAO7B,MAAM,cAAc,WAA+C;EACjE,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,UAAU;GAAG,SAAS;EAAe,CAAC;CACpE;;;;;;;CAQA,MAAM,qBACJ,WACiC;EAMjC,QAAO,MALe,KAAK,KAAK;GAC9B,OAAO;IAAE;IAAW,QAAQ;GAAS;GACrC,SAAS;GACT,OAAO;EACT,CAAC,EAAA,CACc,MAAM;CACvB;;;;;CAMA,MAAM,aAAa,QAA+C;EAChE,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,OAAO;GAAG,SAAS;EAAgB,CAAC;CAClE;AACF;;;;;;;;;;;;;;;;;;ACOO,IAAM,UAAN,cAAsB,WAAW;CAGtC,WAA0B;CAU1B,QAAgB;CAUhB,iBAAyB;CAQzB,aAAqB;;;;;;;;CASrB,MAAc;CAUd,eAAuB;;;;;CAMvB,QAAgB;CAEhB,YAAY,UAA0B,CAAC,GAAG;EACxC,MAAM,OAAO;EACb,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;EACtD,IAAI,QAAQ,mBAAmB,KAAA,GAC7B,KAAK,iBAAiB,QAAQ;EAChC,IAAI,QAAQ,eAAe,KAAA,GAAW,KAAK,aAAa,QAAQ;EAChE,IAAI,QAAQ,QAAQ,KAAA,GAAW,KAAK,MAAM,QAAQ;EAClD,IAAI,QAAQ,iBAAiB,KAAA,GAC3B,KAAK,eAAe,QAAQ;EAC9B,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;CACxD;;;;;;;CAQA,sBAA8B;EAG5B,OAFY,OAAO,KAAK,cAAc,CAE/B,KAAO,IADA,OAAO,KAAK,gBAAgB,CACxB,IAAQ;CAC5B;;;;;;;CAQA,MAAyB,qBAAoC;EAC3D,MAAM,MAAM,mBAAmB;EAE/B,MAAM,EAAE,oBAAoB,MAAA,QAAA,QAAA,CAAA,CAAA,WAAA,uBAAA;EAE5B,OAAM,MADgB,gBAAgB,OAAO,EAAE,IAAI,KAAK,GAAG,CAAC,EAAA,CAC9C,kBAAkB,IAAI;CACtC;AACF;AAhG0C,gBAA7B,SAAoC,mBAAkB,mCAAA;AAGjE,kBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GAFjB,QAGX,WAAA,YAAA,CAAA;AAUA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAZd,QAaX,WAAA,SAAA,CAAA;AAUA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAtBd,QAuBX,WAAA,kBAAA,CAAA;AAQA,kBAAA,CADC,MAAM,EAAE,MAAM,UAAU,CAAC,CAAA,GA9Bf,QA+BX,WAAA,cAAA,CAAA;AAmBA,kBAAA,CADC,MAAM,EAAE,MAAM,UAAU,CAAC,CAAA,GAjDf,QAkDX,WAAA,gBAAA,CAAA;AAlDW,UAAN,kBAAA,CAbN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAMX,iBAAiB;EAAC;EAAU;EAAoB;CAAW;CAC3D,KAAK,EAAE,SAAS;EAAC;EAAQ;EAAO;EAAU;CAAQ,EAAE;CACpD,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,cAAc,KAAK;AAC5B,CAAC,CAAA,GACY,OAAA;;;AC5CN,IAAM,oBAAN,cAAgC,eAAwB;CAC7D,OAAgB,aAAa;;;;;CAM7B,MAAM,UAAU,OAAmC;EACjD,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,MAAM;GAAG,SAAS;EAAqB,CAAC;CACtE;;;;;;;CAQA,MAAM,gBAAgB,gBAA4C;EAChE,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,eAAe;GAAG,SAAS;EAAY,CAAC;CACtE;AACF;;;ACpBO,IAAM,sBAAN,cAAkC,eAA0B;CACjE,OAAgB,aAAa;;CAG7B,MAAM,WAAW,MAAyC;EAExD,QAAO,MADe,KAAK,KAAK;GAAE,OAAO,EAAE,KAAK;GAAG,OAAO;EAAE,CAAC,EAAA,CAC9C,MAAM;CACvB;;;;;CAMA,MAAM,eACJ,UAAwC,CAAC,GACnB;EACtB,OAAO,KAAK,KAAK;GACf,OAAO,QAAQ,iBAAiB,CAAC,IAAI,EAAE,UAAU,KAAK;GACtD,SAAS,CAAC,gBAAgB,UAAU;EACtC,CAAC;CACH;AACF;;;AChBO,IAAM,oBAAoB;AAEjC,IAAM,QAAQ,MAAA;AAQP,IAAM,eAAe;AAOrB,SAAS,cAAc,OAAuB;CACnD,OAAO,OAAO,MAAM,QAAA,CAAyB,CAAC;AAChD;AAOO,SAAS,aAAa,OAAuB;CAClD,OAAO,OAAO,MAAM,QAAA,CAAyB,CAAA,CAAE,QAAQ,KAAK,EAAE,CAAC;AACjE;AAGO,SAAS,eAAe,YAA4B;CACzD,OAAO,aAAa;AACtB;;;;;;;;;;;;;;;;;;ACEO,IAAM,0BAA0D;CACrE;CACA;CACA;CACA;AACF;AAsBO,IAAM,gBAAN,cAA4B,WAAW;CAG5C,WAA0B;CAO1B,QAAgB;CAIhB,YAAoB;CAIpB,eAAuB;CAIvB,SAA8B;CAE9B,YAAY,UAAgC,CAAC,GAAG;EAC9C,MAAM,OAAO;EACb,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;EACtD,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;EAC9D,IAAI,QAAQ,iBAAiB,KAAA,GAC3B,KAAK,eAAe,QAAQ;EAC9B,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CAC1D;;;;;CAMA,eAAuB;EACrB,OAAO,KAAK,IACV,GACA,cAAc,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,YAAY,CAAC,CAClE;CACF;;CAGA,MAAyB,qBAAoC;EAC3D,MAAM,MAAM,mBAAmB;EAC/B,IAAI,CAAC,wBAAwB,SAAS,KAAK,MAAM,GAC/C,MAAM,IAAI,gBACR,kCAAkC,OAAO,KAAK,MAAM,KACpD,wCACA,EAAE,OAAO,SAAS,CACpB;EACF,MAAM,SAAS,OAAO,KAAK,SAAS;EACpC,MAAM,YAAY,OAAO,KAAK,YAAY;EAC1C,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GACxC,MAAM,IAAI,gBACR,+DACA,wCACA,EAAE,OAAO,YAAY,CACvB;EACF,IAAI,CAAC,OAAO,SAAS,SAAS,KAAK,YAAY,GAC7C,MAAM,IAAI,gBACR,+DACA,wCACA,EAAE,OAAO,eAAe,CAC1B;CACJ;AACF;AArEgD,gBAAnC,eAA0C,mBAAkB,mCAAA;AAGvE,kBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GAFjB,cAGX,WAAA,YAAA,CAAA;AAOA,kBAAA,CADC,WAAW,iBAAiB;CAAE,UAAU;CAAM,UAAU;AAAW,CAAC,CAAA,GAT1D,cAUX,WAAA,SAAA,CAAA;AAIA,kBAAA,CADC,MAAM,EAAE,MAAM,UAAU,CAAC,CAAA,GAbf,cAcX,WAAA,aAAA,CAAA;AAIA,kBAAA,CADC,MAAM,EAAE,MAAM,UAAU,CAAC,CAAA,GAjBf,cAkBX,WAAA,gBAAA,CAAA;AAIA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GArBd,cAsBX,WAAA,UAAA,CAAA;AAtBW,gBAAN,kBAAA,CATN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAGX,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;AAClC,CAAC,CAAA,GACY,aAAA;;;AChEN,IAAM,0BAAN,cAAsC,eAA8B;CACzE,OAAgB,aAAa;;CAG7B,MAAM,UAAU,OAAyC;EACvD,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,MAAM;GAAG,SAAS;EAAkB,CAAC;CACnE;;CAGA,MAAM,aAAa,QAAuD;EACxE,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,OAAO;GAAG,SAAS;EAAiB,CAAC;CACnE;AACF;;;;;;;;;;;;;;;;;;ACkBO,IAAM,0BAAN,cAAsC,WAAW;CAGtD,WAA0B;CAI1B,QAAgB;CAIhB,MAAc;;CAGd,8BAAoB,IAAI,KAAK;CAE7B,YAAY,UAA0C,CAAC,GAAG;EACxD,MAAM,OAAO;EACb,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;EACtD,IAAI,QAAQ,QAAQ,KAAA,GAAW,KAAK,MAAM,QAAQ;EAClD,IAAI,QAAQ,gBAAgB,KAAA,GAC1B,KAAK,cACH,QAAQ,uBAAuB,OAC3B,QAAQ,cACR,IAAI,KAAK,QAAQ,WAAW;CACtC;;CAGA,MAAyB,qBAAoC;EAC3D,MAAM,MAAM,mBAAmB;EAC/B,MAAM,MAAM,OAAO,KAAK,GAAG;EAC3B,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,GAClC,MAAM,IAAI,gBACR,oDACA,wCACA,EAAE,OAAO,MAAM,CACjB;CACJ;AACF;AAvC0D,gBAA7C,yBAAoD,mBAAkB,mCAAA;AAGjF,kBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GAFjB,wBAGX,WAAA,YAAA,CAAA;AAIA,kBAAA,CADC,WAAW,eAAe;CAAE,UAAU;CAAM,UAAU;AAAU,CAAC,CAAA,GANvD,wBAOX,WAAA,SAAA,CAAA;AAIA,kBAAA,CADC,MAAM,EAAE,MAAM,UAAU,CAAC,CAAA,GAVf,wBAWX,WAAA,OAAA,CAAA;AAXW,0BAAN,kBAAA,CARN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAEX,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;AAClC,CAAC,CAAA,GACY,uBAAA;;;AChCN,IAAM,oCAAN,cAAgD,eAAwC;CAC7F,OAAgB,aAAa;;CAG7B,MAAM,UAAU,OAAmD;EACjE,OAAO,KAAK,KAAK;GACf,OAAO,EAAE,MAAM;GACf,SAAS,CAAC,mBAAmB,gBAAgB;EAC/C,CAAC;CACH;AACF;;;;;;;;;;;;;;;;;;ACyBO,IAAM,cAAN,cAA0B,WAAW;CAG1C,WAA0B;CAO1B,QAAgB;CAIhB,cAAsB;CAItB,WAAmB;CAOnB,mBAA2B;;CAG3B,QAAgB;CAEhB,YAAY,UAA8B,CAAC,GAAG;EAC5C,MAAM,OAAO;EACb,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;EACtD,IAAI,QAAQ,gBAAgB,KAAA,GAC1B,KAAK,cAAc,QAAQ;EAC7B,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,qBAAqB,KAAA,GAC/B,KAAK,mBAAmB,QAAQ;EAClC,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;CACxD;AACF;AAzC8C,cAAjC,aAAwC,mBAAkB,mCAAA;AAGrE,gBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GAFjB,YAGX,WAAA,YAAA,CAAA;AAOA,gBAAA,CADC,WAAW,iBAAiB;CAAE,UAAU;CAAM,UAAU;AAAU,CAAC,CAAA,GATzD,YAUX,WAAA,SAAA,CAAA;AAIA,gBAAA,CADC,WAAW,WAAW;CAAE,UAAU;CAAM,UAAU;AAAW,CAAC,CAAA,GAbpD,YAcX,WAAA,eAAA,CAAA;AAIA,gBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAjBd,YAkBX,WAAA,YAAA,CAAA;AAOA,gBAAA,CADC,MAAM,EAAE,MAAM,UAAU,CAAC,CAAA,GAxBf,YAyBX,WAAA,oBAAA,CAAA;AAzBW,cAAN,gBAAA,CATN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAEX,iBAAiB;EAAC;EAAU;EAAY;CAAW;CACnD,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,cAAc,KAAK;AAC5B,CAAC,CAAA,GACY,WAAA;;;ACpCN,IAAM,wBAAN,cAAoC,eAA4B;CACrE,OAAgB,aAAa;;CAG7B,MAAM,UAAU,OAAuC;EACrD,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,MAAM;GAAG,SAAS;EAAe,CAAC;CAChE;;CAGA,MAAM,gBAAgB,aAA6C;EACjE,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,YAAY;GAAG,SAAS;EAAY,CAAC;CACnE;AACF;;;AC0EO,IAAM,yBAAN,cAAqC,MAAM;CAGhD,YAA4B,aAAqB;EAC/C,MAAM,wBAAwB,aAAa;EADjB,KAAA,cAAA;CAE5B;CAF4B;CAFnB,OAAO;AAKlB;AAGO,IAAM,8BAAN,cAA0C,MAAM;CAGrD,YAA4B,MAAc;EACxC,MAAM,2BAA2B,KAAI,kBAAmB;EAD9B,KAAA,OAAA;CAE5B;CAF4B;CAFnB,OAAO;AAKlB;AAGO,IAAM,wBAAN,cAAoC,MAAM;CAG/C,YAA4B,aAAqB;EAC/C,MACE,aAAa,YAAW,8CAC1B;EAH0B,KAAA,cAAA;CAI5B;CAJ4B;CAFnB,OAAO;AAOlB;AAGO,IAAM,6BAAN,cAAyC,MAAM;CAC3C,OAAO;AAClB;;;ACtDO,IAAM,6BAAN,cAAyC,MAAM;CAGpD,YAA4B,OAAe;EACzC,MAAM,6BAA6B,OAAO;EADhB,KAAA,QAAA;CAE5B;CAF4B;CAFnB,OAAO;AAKlB;AAGO,IAAM,0BAAN,cAAsC,MAAM;CAGjD,YACkB,OAEA,QAEA,QAChB;EACA,MAAM,kBAAkB,MAAK,MAAO,OAAM,WAAY,OAAM,KAAM;EANlD,KAAA,QAAA;EAEA,KAAA,SAAA;EAEA,KAAA,SAAA;CAGlB;CAPkB;CAEA;CAEA;CAPT,OAAO;AAWlB;AAGO,IAAM,mCAAN,cAA+C,MAAM;CAG1D,YACkB,OAEA,KAEA,WAChB;EACA,MACE,kBAAkB,MAAK,OAAQ,UAAS,gCAAiC,IAAG,EAC9E;EARgB,KAAA,QAAA;EAEA,KAAA,MAAA;EAEA,KAAA,YAAA;CAKlB;CATkB;CAEA;CAEA;CAPT,OAAO;AAalB;AAGO,IAAM,iCAAN,cAA6C,MAAM;CAC/C,OAAO;AAClB;;;AC1FO,SAAS,iBACd,OACA,MACY;CACZ,MAAM,QAAQ,SAAS,iBAAiB,CAAA,EAAG,YAAY;CACvD,OAAO,QAAQ,qBAAqB,OAAO,IAAI,IAAI,KAAK;AAC1D;AAGO,SAAS,cACd,KACA,OACS;CACT,MAAM,YAAY,IAAI,YAAY;CAClC,OAAO,cAAc,QAAQ,cAAc;AAC7C;;;;;;;;ACkFO,IAAM,gBAAN,cAA4B,gBAAgB;CAGjD,YAEkB,OAEA,MAChB;EACA,MAAM,QAAQ,KAAK,EAAC,EAAG,QAAQ;EAC/B,MACE,aAAa,MAAK,0BAA2B,KAC1C,KAAK,UAAU,MAAM,IAAI,CAAA,CACzB,KAAK,KAAK,KACb,2BACA;GAAE;GAAO;EAAK,CAChB;EAXgB,KAAA,QAAA;EAEA,KAAA,OAAA;CAUlB;CAZkB;CAEA;CANT,OAAO;AAiBlB;AAwBA,IAAM,iBACJ;AAOF,SAAS,UACP,KACA,SACS;CACT,IAAI,QAAQ,aAAa,KAAA,GAAW,OAAO;CAC3C,MAAM,YAAY,IAAI,YAAY;CAClC,OAAO,cAAc,QAAQ,cAAc,QAAQ;AACrD;AAEA,SAAS,OAAO,OAAmD;CACjE,OAAO,OAAO,UAAU,YAAY,eAAa,KAAK,KAAK;AAC7D;AAeO,IAAM,kBAAN,MAAM,gBAAgB;CACnB,YAEU,UAEA,MAEA,MAEA,OAChB;EAPgB,KAAA,WAAA;EAEA,KAAA,OAAA;EAEA,KAAA,OAAA;EAEA,KAAA,QAAA;CACf;CAPe;CAEA;CAEA;CAEA;;CAIlB,aAAa,OACX,SAC0B;EAI1B,MAAM,KAAM,MAAM,gBAAgB,QAAQ,EAAE;EAC5C,MAAM,CAAC,UAAU,MAAM,MAAM,SAAS,MAAM,QAAQ,IAAI;GACtD,kBAAkB,OAAO,EAAE,GAAG,CAAC;GAC/B,cAAc,OAAO,EAAE,GAAG,CAAC;GAC3B,0BAA0B,OAAO,EAAE,GAAG,CAAC;GACvC,kBAAkB,OAAO,EAAE,GAAG,CAAC;EACjC,CAAC;EACD,OAAO,IAAI,gBAAgB,UAAU,MAAM,MAAM,KAAK;CACxD;;;;;;;;;;;CAYA,MAAM,iBACJ,OACA,UAAmC,CAAC,GACR;EAC5B,MAAM,WAAWA,UAAwC;GACvD;GACA,MAAM;GACN,KAAAA;GACA,SAAS;GACT,UAAU;GACV,WAAW;GACX,WAAW;EACb;EAEA,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO,QAAQ,IAAI;EAIvC,MAAM,MAAM,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,OAAO;EACrD,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,OAAO,GAAG,OAAO,QAAQ,IAAI;EACzD,IAAI,CAAC,OAAO,IAAI,SAAS,GAAG,OAAO,QAAQ,GAAG;EAC9C,MAAM,UAAU,MAAM,KAAK,KAAK,KAAK,UAAU,IAAI,WAAW,OAAO;EACrE,IAAI,CAAC,WAAW,CAAC,UAAU,SAAS,OAAO,GAAG,OAAO,QAAQ,GAAG;EAEhE,IAAI,mBAAmB,UAAU;GAC/B,MAAM,YAAY,MAAM,KAAK,cAAc,QAAQ,IAAK,OAAO;GAC/D,OAAO;IACL;IACA,MAAM;IACN;IACA;IACA,UAAU;IACV;IACA,WAAW,cAAc;GAC3B;EACF;EAEA,OAAO;GACL;GACA,MAAM,mBAAmB,WAAW,aAAa;GACjD;GACA;GACA,UAAU;GACV,WAAW;GACX,WAAW;EACb;CACF;;;;;CAMA,MAAM,cACJ,WACA,UAAmC,CAAC,GACH;EACjC,MAAM,QAAQ,QAAQ;EACtB,IAAI,UAAU,KAAA,GAAW,OAAO,KAAK,KAAK,qBAAqB,SAAS;EACxE,MAAM,SAAS,MAAM,iBAAiB,aACpC,KAAK,KAAK,KAAK;GACb,OAAO;IAAE;IAAW,QAAQ;GAAS;GACrC,SAAS;EACX,CAAC,CACH;EACA,OACE,OAAO,MAAM,SAAS,IAAI,YAAY,UAAU,KAAK,KACrD,OAAO,MAAM,SAAS,IAAI,YAAY,UAAU,IAAI,KACpD;CAEJ;;;;;CAMA,MAAc,KACZ,YAIA,IACA,SACmB;EACnB,MAAM,QAAQ,QAAQ;EACtB,IAAI,UAAU,KAAA,GAAW,OAAO,WAAW,IAAI,EAAE,GAAG,CAAC;EAIrD,QAAO,MAHY,iBAAiB,aAClC,WAAW,KAAK;GAAE,OAAO,EAAE,GAAG;GAAG,OAAO;EAAE,CAAC,CAC7C,EAAA,CACY,MAAM;CACpB;;CAGA,MAAM,WAAW,OAAiC;EAChD,QAAQ,MAAM,KAAK,iBAAiB,KAAK,EAAA,CAAG,SAAS;CACvD;;;;;;;;CASA,MAAM,iBAAiB,OAAuC;EAC5D,MAAM,MAAM,OAAO,KAAK,IAAI,MAAM,KAAK,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI;EACjE,IAAI,CAAC,KAAK,MAAM,IAAI,iBAAiB,KAAK;EAC1C,MAAM,wBAAQ,IAAI,IAAwC;EAC1D,MAAM,QAAQ,EAAE,UAAU,IAAI,YAAY,KAAK;EAC/C,MAAM,SACJ,MAAM,iBAAiB,MAAM,gBAAgB,KAAK,MAAM,UAAU,KAAK,CAAC,EAAA,CACxE,QAAQ,SAAS,UAAU,MAAM,KAAK,CAAC;EACzC,MAAM,WAAgC,CAAC;EACvC,KAAA,MAAW,QAAQ,OACjB,SAAS,KAAK;GACZ;GACA,WAAW,MAAM,KAAK,cAAc,KAAK,gBAAgB,OAAO,KAAK;EACvE,CAAC;EAEH,OAAO;GAAE;GAAK,OAAO;EAAS;CAChC;;;;;;;;CASA,MAAM,UACJ,WACA,iBACA,UAAmC,CAAC,GACC;EACrC,MAAM,wBAAQ,IAAI,IAAwC;EAC1D,MAAM,0BAAU,IAAI,IAAY;EAEhC,MAAM,OAAO,OACX,QACAC,WAC8B;GAC9B,KAAA,MAAW,SAAS,QAAQ;IAC1B,MAAM,YAAY,MAAM,KAAK,cAAc,OAAO,OAAO,OAAO;IAChE,MAAM,UAAU,UAAU;IAC1B,IAAI,CAAC,SAAS,IAAI;IAClB,IAAI,QAAQ,OAAO,WAAW,OAAO,CAAC,GAAGA,QAAO,OAAO;IACvD,IAAI,CAAC,UAAU,aAAa,QAAQ,IAAI,QAAQ,EAAE,GAAG;IACrD,QAAQ,IAAI,QAAQ,EAAE;IACtB,MAAM,SAAS,UAAU,UAAU;IACnC,MAAM,SACJ,QAAQ,aAAa,KAAA,IACjB,MAAM,KAAK,MAAM,UAAU,MAAM,IACjC,MAAM,iBAAiB,QAAQ,gBAC7B,KAAK,MAAM,UAAU,MAAM,CAC7B,EAAA,CACJ,QAAQ,SAAS,UAAU,MAAM,OAAO,CAAC;IAC3C,MAAM,QAAQ,MAAM,KAClB,MAAM,KAAK,SAAS,KAAK,cAAc,GACvC,CAAC,GAAGA,QAAO,OAAO,CACpB;IACA,IAAI,OAAO,OAAO;GACpB;GACA,OAAO;EACT;EAEA,MAAM,QAAQ,MAAM,KAAK,iBAAiB,CAAC,CAAC;EAC5C,IAAI,CAAC,OAAO,OAAO;EAEnB,OAAO,CADM,MAAM,MAAM,SAAS,IACpB,GAAG,KAAK,CAAA,CAAE,KAAK,aAAa;GACxC,WAAW,QAAQ;GACnB,MAAM,QAAQ,QAAQ,QAAQ;EAChC,EAAE;CACJ;;;;;;;;;CAUA,MAAM,kBACJ,MACe;EACf,IAAI,CAAC,OAAO,KAAK,KAAK,GAAG;EACzB,MAAM,MAAM,MAAM,KAAK,KAAK,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;EAClD,IAAI,CAAC,KAAK;EACV,MAAM,OAAO,MAAM,KAAK,UAAU,IAAI,WAAW,CAAC,KAAK,cAAc,GAAG,EACtE,UAAU,IAAI,YAAY,KAC5B,CAAC;EACD,IAAI,MAAM,MAAM,IAAI,cAAc,KAAK,OAAO,IAAI;CACpD;;;;;;;;;;;;;;CAeA,MAAM,kBACJ,KAIe;EACf,IAAI,IAAI,WAAW,UAAU;EAC7B,MAAM,0BAAU,IAAI,IAAY;EAChC,IAAI,OAAO,IAAI,EAAE,GAAG,QAAQ,IAAI,IAAI,EAAE;EAGtC,MAAM,WAAW,IAAI,YAAY,iBAAiB,CAAA,EAAG,YAAY;EACjE,MAAM,UAAU,MAAM,iBAAiB,gBACrC,KAAK,KAAK,KAAK,EACb,OAAO;GAAE,WAAW,IAAI;GAAW,SAAS,IAAI;EAAQ,EAC1D,CAAC,CACH;EACA,KAAA,MAAW,UAAU,SACnB,IAAI,OAAO,OAAO,OAAO,YAAY,UAAU,UAC7C,QAAQ,IAAI,OAAO,EAAE;EAEzB,MAAM,kBAA4B,CAAC;EACnC,KAAA,MAAW,UAAU,SACnB,KAAA,MAAW,QAAQ,MAAM,iBAAiB,gBACxC,KAAK,MAAM,UAAU,MAAM,CAC7B,GACE,IAAI,UAAU,MAAM,EAAE,SAAS,CAAC,GAC9B,gBAAgB,KAAK,KAAK,cAAc;EAE9C,IAAI,gBAAgB,WAAW,GAAG;EAClC,MAAM,OAAO,MAAM,KAAK,UAAU,IAAI,WAAW,iBAAiB,EAChE,SACF,CAAC;EACD,IAAI,MAAM,MAAM,IAAI,cAAc,IAAI,MAAM,CAAC,GAAG,OAAO,CAAA,CAAE,MAAM,IAAI,IAAI;CACzE;CAEQ,cACN,OACA,OACA,SAC4B;EAC5B,IAAI,UAAU,MAAM,IAAI,KAAK;EAC7B,IAAI,CAAC,SAAS;GACZ,UAAU,KAAK,iBAAiB,OAAO,OAAO;GAC9C,MAAM,IAAI,OAAO,OAAO;EAC1B;EACA,OAAO;CACT;AACF;AAGO,SAAS,sBACd,SAC0B;CAC1B,OAAO,gBAAgB,OAAO,OAAO;AACvC;;;AC5cO,IAAM,yBAAN,cAAqC,cAAc;CAC/C,OAAO;CAEhB,YAAY,OAAe,MAA2B;EACpD,MAAM,OAAO,IAAI;EACjB,KAAK,UAAU,wCAAwC,KACpD,KAAK,UAAU,MAAM,IAAI,CAAA,CACzB,KAAK,KAAK;CACf;AACF;AAEA,IAAM,iBACJ;AAMK,SAAS,YAAY,QAA6C;CACvE,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,IAAI,WAAW,OAAO,OAAA;CACtB,IAAI,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,KAAK,SAAA,IAC7C,MAAM,IAAI,MACR,6DAAiF,OAAO,MAAM,EAAC,EACjG;CACF,OAAO;AACT;AAGO,SAAS,aAAa,QAA8C;CACzE,OAAO,YAAY,MAAM,IAAI;AAC/B;AAGO,IAAM,gBAAN,MAAoB;CAOzB,YACmB,YAER,KAEA,QACT;EALiB,KAAA,aAAA;EAER,KAAA,MAAA;EAEA,KAAA,SAAA;EAET,KAAK,WAAW,YAAY,MAAM;CACpC;CAPmB;CAER;CAEA;CAXM,6BAAa,IAAI,IAAwC;CACzD,wBAAQ,IAAI,IAAgC;CACrD,YAAY;;CAEX;;;;;CAgBT,QAAQ,OAA2C;EACjD,IAAI,UAAU,KAAK,WAAW,IAAI,KAAK;EACvC,IAAI,CAAC,SAAS;GACZ,UAAU,KAAK,WAAW,iBAAiB,OAAO,EAChD,UAAU,KAAK,IAAI,YAAY,KACjC,CAAC;GACD,KAAK,WAAW,IAAI,OAAO,OAAO;EACpC;EACA,OAAO;CACT;;;;;;CAOA,MAAM,QAAQ,OAAmC;EAC/C,IAAI,UAAU,KAAK,MAAM,IAAI,KAAK;EAClC,IAAI,CAAC,SAAS;GAGZ,MAAM,WAAW,KAAK,IAAI,YAAY;GACtC,UAAU,iBAAiB,gBACzB,KAAK,WAAW,MAAM,UAAU,KAAK,CACvC,CAAA,CAAE,MAAMC,WAAUA,OAAM,QAAQ,SAAS,cAAc,MAAM,QAAQ,CAAC,CAAC;GACvE,KAAK,MAAM,IAAI,OAAO,OAAO;EAC/B;EACA,MAAM,QAAQ,MAAM;EACpB,KAAK,aAAa,MAAM;EACxB,IAAI,KAAK,YAAA,KACP,MAAM,IAAI,uBAAuB,KAAK,IAAI,IAAc,OAAO;EACjE,OAAO;CACT;;CAGA,MAAM,OAAoC;EACxC,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI,OAAO;EACX,IAAI,eAAa,KAAK,SAAS,GAAG;GAEhC,MAAM,QAAQ,KAAK,IAAI,YAAY;GACnC,MAAM,CAAC,WAAW,MAAM,iBAAiB,aACvC,KAAK,WAAW,SAAS,KAAK;IAAE,OAAO,EAAE,IAAI,UAAU;IAAG,OAAO;GAAE,CAAC,CACtE;GACA,IAAI,SAAS,QAAQ,cAAc,SAAS,KAAK,GAAG,OAAO,QAAQ;EACrE;EACA,OAAO;GAAE,OAAO,KAAK,IAAI;GAAc;GAAW;EAAK;CACzD;;;;;;;;;;CAWA,KACE,WACA,OACA,MAC2B;EAC3B,MAAM,MAAM,UAAU;EACtB,MAAM,UAAU,UAAU;EAC1B,IAAI,CAAC,UAAU,aAAa,CAAC,KAAK,MAAM,CAAC,SAAS,IAAI,OAAO;EAC7D,MAAM,OAAO,QAAQ,QAAQ,QAAQ;EACrC,MAAM,KAAK,KAAK,WAAW,UAAU,MAAM,cAAc,QAAQ,EAAE;EACnE,IAAI,MAAM,GACR,MAAM,IAAI,uBAAuB,KAAK,IAAI,IAAc,CACtD,GAAG,KACA,MAAM,EAAE,CAAA,CACR,KAAK,WAAW;GAAE,WAAW,MAAM;GAAW,MAAM,MAAM;EAAK,EAAE,GACpE;GAAE,WAAW,QAAQ;GAAI;EAAK,CAChC,CAAC;EACH,IAAI,SAAS,KAAK,UAAU;GAC1B,IAAI,KAAK,WAAW,OAClB,MAAM,IAAI,uBAAuB,KAAK,IAAI,IAAc,OAAO;GACjE,OAAO;EACT;EACA,OAAO;GAAE,OAAO,IAAI;GAAI,WAAW,QAAQ;GAAI;EAAK;CACtD;AACF;;;ACvCO,IAAM,aAAN,MAAM,WAAW;CACd,YACU,MACA,OACA,cACC,cACD,cACA,YACC,cAED,YAChB;EATgB,KAAA,OAAA;EACA,KAAA,QAAA;EACA,KAAA,eAAA;EACC,KAAA,eAAA;EACD,KAAA,eAAA;EACA,KAAA,aAAA;EACC,KAAA,eAAA;EAED,KAAA,aAAA;CACf;CATe;CACA;CACA;CACC;CACD;CACA;CACC;CAED;;CAIlB,aAAa,OAAO,SAAiD;EACnE,MAAM,eACJ,QAAQ,gBAAiB,MAAM,oBAAkB,OAAO;EAO1D,MAAM,WAAY,MAAM,gBACtB,QAAQ,eAAgB,QAAQ,MAAM,aAAa,KAAM,aAAa,EACxE;EACA,MAAM,CAAC,MAAM,OAAO,cAAc,YAAY,cAC5C,MAAM,QAAQ,IAAI;GAChB,0BAA0B,OAAO,EAAE,IAAI,SAAS,CAAC;GACjD,kBAAkB,OAAO,EAAE,IAAI,SAAS,CAAC;GACzC,sBAAsB,OAAO,EAAE,IAAI,SAAS,CAAC;GAC7C,oBAAoB,OAAO,EAAE,IAAI,SAAS,CAAC;GAC3C,gBAAgB,OAAO,EAAE,IAAI,SAAS,CAAC;EACzC,CAAC;EACH,OAAO,IAAI,WACT,MACA,OACA,cACA,QAAQ,cACR,cACA,YACA,QAAQ,cACR,UACF;CACF;;;;;;;;;;;;;;;;;;;;;CAsBA,MAAM,oBACJ,OACA,UAA4B,CAAC,GACL;EACxB,IAAI,aAAa,QAAQ,MAAM,GAC7B,OAAO,KAAK,sBAAsB,OAAO,OAAO;EAClD,MAAM,MAAM,MAAM,KAAK,WAAW,KAAK;EACvC,MAAM,QAAQ,MAAM,KAAK,QAAQ,GAAG;EACpC,MAAM,gBAA+B,CAAC;EACtC,IAAI,YAAY;EAChB,IAAI,kBAAkB;EAEtB,KAAA,MAAW,QAAQ,OAAO;GACxB,MAAM,eAAe,KAAK,oBAAoB;GAC9C,MAAM,WAAW,MAAM,KAAK,YAAY,KAAK,cAAc;GAC3D,MAAM,WAAW,YAAY;GAC7B,MAAM,kBAAkB,aAAa,QAAQ,aAAa,KAAA;GAC1D,MAAM,WAAW,WAAW;GAC5B,IAAI,iBACF,kBAAkB;QAElB,aAAa;GAGf,cAAc,KAAK;IACjB,gBAAgB,KAAK;IACrB,YAAY,OAAO,KAAK,cAAc,CAAC;IACvC,cAAc,OAAO,KAAK,gBAAgB,CAAC;IAC3C;IACA;IACA;IACA,KAAK,KAAK;IACV;GACF,CAAC;EACH;EAEA,OAAO;GACL;GACA;GACA,UAAU,IAAI,YAAY;GAC1B;GACA;EACF;CACF;;;;;;;;;;;;;;CAeA,MAAM,sBACJ,OACA,UAA4B,CAAC,GACJ;EACzB,IAAI,aAAa,QAAQ,MAAM,GAC7B,OAAO,KAAK,wBAAwB,OAAO,OAAO;EACpD,OAAO,KAAK,UAAU,MAAM,KAAK,WAAW,KAAK,CAAC;CACpD;;CAGA,MAAc,UAAU,KAA+C;EACrE,MAAM,QAAQ,IAAI;EAGlB,MAAM,QAAQ,IAAI,YAAY;EAC9B,MAAM,WACJ,MAAM,iBAAiB,aAAa,KAAK,aAAa,UAAU,KAAK,CAAC,EAAA,CACtE,QAAQ,SAAS,cAAc,MAAM,KAAK,CAAC;EAC7C,MAAM,QAA+B,CAAC;EACtC,IAAI,eAAe;EACnB,IAAI,YAAY;EAChB,IAAI,kBAAkB;EAEtB,KAAA,MAAW,QAAQ,SAAS;GAC1B,MAAM,CAAC,cACL,MAAM,iBAAiB,aACrB,KAAK,WAAW,KAAK;IAAE,OAAO,EAAE,IAAI,KAAK,YAAY;IAAG,OAAO;GAAE,CAAC,CACpE,EAAA,CACA,QAAQ,QAAQ,cAAc,KAAK,KAAK,CAAC;GAC3C,MAAM,UAAU,OAAO,KAAK,oBAAoB,CAAC;GACjD,MAAM,OAAO,YAAY,MAAM,KAAK,YAAY,SAAS,IAAI;GAC7D,MAAM,kBAAkB,SAAS;GACjC,MAAM,WAAW,SAAS,OAAO,IAAK,UAAU,KAAM;GACtD,gBAAgB;GAChB,IAAI,iBAAiB,kBAAkB;QAClC,aAAa;GAClB,MAAM,KAAK;IACT,QAAQ,KAAK;IACb,UAAU,KAAK;IACf,aAAa,KAAK;IAClB,eAAe,WAAW,QAAQ;IAClC,eAAe,WAAW,QAAQ;IAClC,kBAAkB;IAClB,YAAY,QAAQ;IACpB;IACA;GACF,CAAC;EACH;EAEA,OAAO;GACL;GACA,YAAY,MAAM,SAAS;GAC3B;GACA;GACA,UAAU,IAAI,YAAY;GAC1B;GACA;EACF;CACF;;;;;;;;;;;;;;;;;;CAmBA,MAAM,oBACJ,OACA,KACA,UAA4B,CAAC,GACG;EAChC,IAAI,aAAa,QAAQ,MAAM,GAC7B,QAAQ,MAAM,KAAK,QAAQ,OAAO,KAAK,OAAO,EAAA,CAAG;EACnD,oBAAkB,KAAK,qBAAqB;EAC5C,MAAM,QAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK,WAAW,KAAK,CAAC;EAK7D,MAAM,4BAAY,IAAI,IAAiC;EACvD,KAAA,MAAW,QAAQ,OAAO;GACxB,MAAM,eAAe,KAAK,oBAAoB,IAAI;GAClD,MAAM,WAAW,UAAU,IAAI,KAAK,cAAc;GAClD,IAAI,UACF,SAAS,YAAY;QAErB,UAAU,IAAI,KAAK,gBAAgB;IACjC,gBAAgB,KAAK;IACrB,UAAU;IACV,KAAK,KAAK;GACZ,CAAC;EAEL;EAEA,OAAO,MAAM,KAAK,UAAU,OAAO,CAAC;CACtC;;;;;;;;;;;;;;;;;;;;;CAsBA,MAAM,WACJ,OACA,KACA,UAA4B,CAAC,GACF;EAC3B,IAAI,aAAa,QAAQ,MAAM,GAAG;GAChC,MAAM,OAAO,MAAM,KAAK,iBAAiB,OAAO,KAAK,OAAO;GAC5D,OAAO,KAAK,KACR;IAAE,IAAI;IAAM,WAAW,CAAC;GAAE,IAC1B;IAAE,IAAI;IAAO,WAAW,KAAK;GAAU;EAC7C;EACA,MAAM,eAAe,MAAM,KAAK,oBAAoB,OAAO,GAAG;EAO9D,MAAM,iBAAiB,MAAM,QAAQ,IACnC,aAAa,IAAI,OAAO,iBAAiB;GACvC;GACA,WAAW,MAAM,KAAK,aAAa,OAAO,YACxC,YAAY,gBACZ,WACF;EACF,EAAE,CACJ;EAEA,MAAM,YAAgC,CAAC;EACvC,KAAA,MAAW,EAAE,aAAa,eAAe,gBACvC,IAAI,YAAY,YAAY,UAC1B,UAAU,KAAK;GACb,gBAAgB,YAAY;GAC5B,WAAW,YAAY;GACvB;EACF,CAAC;EAIL,IAAI,UAAU,WAAW,GACvB,OAAO;GAAE,IAAI;GAAM,WAAW,CAAC;EAAE;EAEnC,OAAO;GAAE,IAAI;GAAO;EAAU;CAChC;;;;;;;;;;;;;;;CAgBA,MAAM,QACJ,OACA,KACA,UAA4B,CAAC,GACT;EACpB,oBAAkB,KAAK,SAAS;EAChC,MAAM,SAAS,QAAQ,UAAU;EACjC,MAAM,MAAM,MAAM,KAAK,WAAW,KAAK;EACvC,MAAM,OAAO,IAAI,cAAc,KAAK,YAAY,KAAK,MAAM;EAC3D,MAAM,QAAwB,CAAC;EAE/B,MAAM,QAAQ,OACZ,QACA,OACA,MACA,cACkB;GAClB,KAAA,MAAW,QAAQ,MAAM,KAAK,QAAQ,MAAM,GAAG;IAC7C,MAAM,YAAY,MAAM,KAAK,QAAQ,KAAK,cAAc;IACxD,MAAM,MAAM,KAAK,KAAK,WAAW,OAAO,IAAI;IAC5C,MAAM,WAAW,aAAa,MAAM,WAAW,OAAO,MAAM,SAAS;IACrE,SAAS,WAAW,QAAQ;IAC5B,MAAM,KAAK,QAAQ;IACnB,IAAI,KACF,MAAM,MAAM,IAAI,OAAO,QAAQ,GAAG,CAAC,GAAG,MAAM,GAAG,GAAG,SAAS,QAAQ;GACvE;EACF;EACA,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,GAAG,GAAG;EAE9C,OAAO;GACL;GACA,WAAW,IAAI;GACf;GACA;GACA;GACA,QAAQ,gBAAgB,MAAM,QAAQ,SAAS,CAAC,KAAK,QAAQ,CAAC;EAChE;CACF;;;;;;;;;;;;;;CAeA,MAAM,iBACJ,OACA,KACA,UAA4B,CAAC,GACF;EAC3B,oBAAkB,KAAK,kBAAkB;EACzC,MAAM,SAAS,QAAQ,UAAU;EACjC,MAAM,MAAM,MAAM,KAAK,WAAW,KAAK;EACvC,MAAM,OAAO,IAAI,cAAc,KAAK,YAAY,KAAK,MAAM;EAC3D,MAAM,QAAuB,CAAC;EAC9B,MAAM,YAAgC,CAAC;EACvC,MAAM,8BAAc,IAAI,IAA6B;EACrD,MAAM,WAAW,IAAI,YAAY;EACjC,MAAM,gBAAgB,UAAmC;GACvD,IAAI,UAAU,YAAY,IAAI,KAAK;GACnC,IAAI,CAAC,SAAS;IACZ,UAAU,KAAK,kBAAkB,OAAO,QAAQ;IAChD,YAAY,IAAI,OAAO,OAAO;GAChC;GACA,OAAO;EACT;EAEA,MAAM,QAAQ,OACZ,QACA,OACA,MACA,cACkB;GAClB,KAAA,MAAW,QAAQ,MAAM,KAAK,QAAQ,MAAM,GAAG;IAC7C,MAAM,YAAY,MAAM,KAAK,QAAQ,KAAK,cAAc;IACxD,MAAM,OAAO,aAAa,MAAM,WAAW,OAAO,MAAM,SAAS;IACjE,MAAM,YAAY,MAAM,aAAa,KAAK,cAAc;IACxD,MAAM,YAAY,KAAK,IAAI,WAAW,KAAK,QAAQ;IACnD,YAAY,IACV,KAAK,gBACL,QAAQ,QAAQ,YAAY,SAAS,CACvC;IAGA,MAAM,QAAQ,KAAK,IAAI,GAAG,cAAc,KAAK,WAAW,SAAS,CAAC;IAClE,MAAM,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,OAAO,IAAI,IAAI;IAC5D,MAAM,KAAK;KAAE,GAAG;KAAM,UAAU,QAAQ;KAAM;KAAW;IAAM,CAAC;IAChE,IAAI,KACF,MAAM,MAAM,IAAI,OAAO,QAAQ,GAAG,CAAC,GAAG,MAAM,GAAG,GAAG,KAAK;SACzD,IAAW,QAAQ,GACjB,UAAU,KAAK;KACb,gBAAgB,KAAK;KACrB,WAAW,KAAK;KAChB;KACA;KACA;KACA,KAAK,KAAK;KACV,WAAW,UAAU;IACvB,CAAC;GAEL;EACF;EACA,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,GAAG,GAAG;EAE9C,OAAO;GACL;GACA,WAAW,IAAI;GACf;GACA;GACA;GACA;GACA,IAAI,UAAU,WAAW;EAC3B;CACF;;;;;;;;;CAcA,MAAc,QAAQ,KAA0C;EAC9D,MAAM,QAAQ,IAAI,YAAY;EAC9B,QACE,MAAM,iBAAiB,aACrB,KAAK,MAAM,UAAU,IAAI,EAAY,CACvC,EAAA,CACA,QAAQ,SAAS,cAAc,MAAM,KAAK,CAAC;CAC/C;CAEA,MAAc,WAAW,OAAyC;EAChE,IAAI,CAAC,OAAO,MAAM,IAAI,iBAAiB,KAAK;EAG5C,MAAM,WAAW,iBAAiB,CAAA,EAAG;EACrC,MAAM,MAAM,YAEN,MAAM,iBAAiB,gBACrB,KAAK,KAAK,KAAK;GAAE,OAAO,EAAE,IAAI,MAAM;GAAG,OAAO;EAAE,CAAC,CACnD,EAAA,CACA,MAAM,QAAQ,cAAc,KAAK,QAAQ,CAAC,KAAK,OACjD,MAAM,KAAK,KAAK,IAAI,KAAK;EAC7B,IAAI,CAAC,KAAK,MAAM,IAAI,iBAAiB,KAAK;EAC1C,OAAO;CACT;;;;;;CAOA,MAAc,kBACZ,OACA,UACiB;EACjB,MAAM,OAAO,MAAM,iBAAiB,gBAClC,KAAK,aAAa,OAAO,KAAK,EAAE,OAAO;GAAE;GAAO,OAAO;EAAY,EAAE,CAAC,CACxE;EACA,IAAI,QAAQ;EACZ,KAAA,MAAW,OAAO,MAChB,IAAI,cAAc,KAAK,QAAQ,GAAG,SAAS,OAAO,IAAI,OAAO,CAAC;EAChE,OAAO,KAAK,IAAI,GAAG,KAAK;CAC1B;;CAGA,MAAc,sBACZ,OACA,SACwB;EACxB,MAAM,MAAM,MAAM,KAAK,WAAW,KAAK;EACvC,MAAM,WAAW,IAAI,YAAY;EACjC,MAAM,OAAO,IAAI,cAAc,KAAK,YAAY,KAAK,QAAQ,UAAU,CAAC;EAExE,MAAM,SAAS,OACb,QACA,OACA,SACuD;GACvD,MAAM,gBAA+B,CAAC;GACtC,IAAI,YAAY;GAChB,IAAI,kBAAkB;GACtB,KAAA,MAAW,QAAQ,MAAM,KAAK,QAAQ,MAAM,GAAG;IAC7C,MAAM,YAAY,MAAM,KAAK,QAAQ,KAAK,cAAc;IACxD,MAAM,MAAM,KAAK,KAAK,WAAW,OAAO,IAAI;IAC5C,MAAM,eAAe,KAAK,oBAAoB;IAC9C,MAAM,QAAqB;KACzB,gBAAgB,KAAK;KACrB,YAAY,OAAO,KAAK,cAAc,CAAC;KACvC,cAAc,OAAO,KAAK,gBAAgB,CAAC;KAC3C;KACA,UAAU;KACV,UAAU;KACV,KAAK,KAAK;KACV,iBAAiB;IACnB;IAGA,MAAM,gBACH,UAAU,WAAW,YAAY,WAAW;IAC/C,IAAI,OAAO,cAAc;KACvB,MAAM,QAAQ,MAAM,OAAO,IAAI,OAAO,QAAQ,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC;KAC/D,MAAM,WAAW,MAAM;KACvB,MAAM,WAAW,MAAM,YAAY;KACnC,MAAM,kBAAkB,MAAM;KAC9B,MAAM,WAAW,IAAI;KACrB,MAAM,aAAa,MAAM;KACzB,aAAa,MAAM;IACrB,OAAO;KACL,MAAM,WAAW,MAAM,KAAK,YAAY,KAAK,cAAc;KAC3D,MAAM,WAAW,YAAY;KAC7B,MAAM,WAAW,MAAM,WAAW;KAClC,MAAM,kBAAkB,aAAa;KACrC,IAAI,CAAC,MAAM,iBAAiB,aAAa,MAAM;IACjD;IACA,IAAI,MAAM,iBAAiB,kBAAkB;IAC7C,cAAc,KAAK,KAAK;GAC1B;GACA,OAAO;IAAE;IAAW;IAAe;GAAgB;EACrD;EAGA,OAAO;GAAE;GAAO;GAAU,GAAG,MADR,OAAO,OAAO,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;EACrB;CACtC;;CAGA,MAAc,wBACZ,OACA,SACyB;EACzB,MAAM,MAAM,MAAM,KAAK,WAAW,KAAK;EACvC,MAAM,OAAO,IAAI,cAAc,KAAK,YAAY,KAAK,QAAQ,UAAU,CAAC;EACxE,MAAM,MAAM,MAAM,KAAK,UAAU,GAAG;EACpC,MAAM,4BAAY,IAAI,IAAqC;EAC3D,MAAM,gBAAqC,CAAC;EAE5C,MAAM,QAAQ,OACZ,QACA,OACA,MACA,UACkB;GAClB,KAAA,MAAW,QAAQ,MAAM,KAAK,QAAQ,MAAM,GAAG;IAC7C,MAAM,YAAY,MAAM,KAAK,QAAQ,KAAK,cAAc;IACxD,MAAM,MAAM,KAAK,KAAK,WAAW,OAAO,IAAI;IAC5C,IAAI,CAAC,OAAO,CAAC,UAAU,WAAW;IAClC,MAAM,UAAU,UAAU;IAC1B,IAAI,WAAW,UAAU,IAAI,IAAI,KAAK;IACtC,IAAI,CAAC,UAAU;KACb,WAAW,KAAK,UAAU,OAAO;KACjC,UAAU,IAAI,IAAI,OAAO,QAAQ;IACnC;IACA,MAAM,eAAe,QAAQ,KAAK,oBAAoB;IACtD,cAAc,KAAK;KACjB,QAAQ,KAAK;KACb,gBAAgB,KAAK;KACrB,MAAM,IAAI;KACV;KACA;KACA;KACA,UAAU,MAAM;IAClB,CAAC;IACD,MAAM,MAAM,IAAI,OAAO,QAAQ,GAAG,CAAC,GAAG,MAAM,GAAG,GAAG,YAAY;GAChE;EACF;EACA,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,GAAG,CAAC;EAE5C,IAAI,eAAe,IAAI;EACvB,IAAI,YAAY,IAAI;EACpB,IAAI,kBAAkB,IAAI;EAC1B,KAAA,MAAW,OAAO,eAAe;GAC/B,gBAAgB,IAAI,eAAe,IAAI,SAAS;GAGhD,IAAI,IAAI,SAAS,aAAa,IAAI,UAAU;IAC1C,aAAa,IAAI,eAAe,IAAI,SAAS;IAC7C,IAAI,IAAI,SAAS,iBAAiB,kBAAkB;GACtD,OAAA,IAAW,IAAI,SAAS,YACtB,kBAAkB;EAEtB;EACA,OAAO;GACL,GAAG;GACH,YACE,IAAI,cAAc,cAAc,MAAM,QAAQ,IAAI,SAAS,UAAU;GACvE;GACA;GACA;GACA;EACF;CACF;;CAGA,MAAc,YACZ,WACwB;EACxB,IAAI,CAAC,KAAK,cAAc,OAAO;EAC/B,MAAM,QAAQ,MAAM,KAAK,aAAa,SAAS;EAC/C,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,CAAC,OAAO,SAAS,KAAK,GACjE,OAAO;EACT,OAAO,OAAO,KAAK;CACrB;;;;;;CAOA,MAAc,YAAY,gBAAgD;EACxE,IAAI,CAAC,KAAK,cAAc,OAAO;EAC/B,MAAM,QAAQ,MAAM,KAAK,aAAa,cAAc;EACpD,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;EAClD,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO;EACpC,OAAO,OAAO,KAAK;CACrB;AACF;AAOA,SAAS,aACP,MACA,WACA,OACA,MACA,WACc;CACd,MAAM,sBAAsB,KAAK,oBAAoB;CACrD,OAAO;EACL,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ;EACA;EACA,gBAAgB,KAAK;EACrB,MAAM,UAAU;EAChB,MAAM,UAAU,SAAS,QAAQ;EACjC,SAAS,UAAU,KAAK,QAAQ;EAChC,YAAY,OAAO,KAAK,cAAc,CAAC;EACvC,cAAc,OAAO,KAAK,gBAAgB,CAAC;EAC3C;EACA,KAAK,KAAK;EACV,UAAU,YAAY;EACtB,WAAW,UAAU;EACrB,UAAU,UAAU,WAAW,MAAM;EACrC,UAAU;CACZ;AACF;AAMA,SAAS,gBAAgB,OAA8C;CACrE,MAAM,yBAAS,IAAI,IAAiC;CACpD,KAAA,MAAW,QAAQ,OAAO;EACxB,MAAM,WAAW,OAAO,IAAI,KAAK,cAAc;EAC/C,IAAI,UAAU,SAAS,YAAY,KAAK;OAEtC,OAAO,IAAI,KAAK,gBAAgB;GAC9B,gBAAgB,KAAK;GACrB,UAAU,KAAK;GACf,KAAK,KAAK;EACZ,CAAC;CACL;CACA,OAAO,MAAM,KAAK,OAAO,OAAO,CAAC;AACnC;AAMA,SAAS,oBAAkB,KAAa,IAAkB;CACxD,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,GAClC,MAAM,IAAI,MAAM,GAAG,GAAE,8CAA+C,IAAG,EAAG;AAE9E;AASA,eAAe,oBACb,SACuB;CACvB,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MACR,8DACF;CAEF,OAAO,mBAAmB,EAAE,IAAI,QAAQ,GAAG,CAAC;AAC9C;AAKA,eAAsB,iBACpB,SACqB;CACrB,OAAO,WAAW,OAAO,OAAO;AAClC;;;AC/oBO,IAAM,oBAAN,MAAM,kBAAkB;CACrB,YAaU,MAKA,OACA,cAChB;EAPgB,KAAA,OAAA;EAKA,KAAA,QAAA;EACA,KAAA,eAAA;CACf;CAPe;CAKA;CACA;;CAIlB,aAAa,OACX,SAC4B;EAC5B,MAAM,eACJ,QAAQ,gBAAiB,MAAM,kBAAkB,OAAO;EAG1D,MAAM,WAAW,QAAQ,MAAM,aAAa;EAC5C,MAAM,CAAC,MAAM,SAAS,MAAM,QAAQ,IAAI,CACtC,0BAA0B,OAAO,EAAE,IAAI,SAAS,CAAC,GACjD,kBAAkB,OAAO,EAAE,IAAI,SAAS,CAAC,CAC3C,CAAC;EACD,OAAO,IAAI,kBAAkB,MAAM,OAAO,YAAY;CACxD;;;;;;;;;;;;;;;;;;;;;;;;CAyBA,MAAM,iBACJ,OACA,SAC0B;EAC1B,MAAM,MAAM,MAAM,KAAK,eAAe,OAAO,OAAO;EACpD,OAAO,KAAK,aAAa,gBAAgB,OAAO,OAC9C,KAAK,qBAAqB,IAAI,GAAG,CACnC;CACF;;;;;;;;;;CAWA,MAAM,qBACJ,OACA,SACwB;EACxB,MAAM,MAAM,eAAe,OAAO,OAAO;EACzC,OAAO,KAAK,aAAa,gBAAgB,OAAO,OAC9C,KAAK,yBAAyB,IAAI,GAAG,CACvC;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BA,MAAM,cACJ,OACA,SAI8B;EAC9B,MAAM,aAAa,MAAM,KAAK,eAAe,OAAO,QAAQ,OAAO;EACnE,MAAM,aAAa,eAAe,OAAO,QAAQ,OAAO;EACxD,OAAO,KAAK,aAAa,gBAAgB,OAAO,OAAO;GAGrD,OAAO;IAAE,UAAA,MAFc,KAAK,qBAAqB,IAAI,UAAU;IAE5C,UAAA,MADI,KAAK,yBAAyB,IAAI,UAAU;GACvC;EAC9B,CAAC;CACH;;;;;;;CAYA,MAAc,eACZ,OACA,SACyB;EACzB,iBAAiB,QAAQ,YAAY,kBAAkB;EACvD,kBAAkB,QAAQ,KAAK,kBAAkB;EACjD,MAAM,UAAU,MAAM,MAAM;EAC5B,IAAI,CAAC,SACH,MAAM,IAAI,MACR,+DACF;EAEF,MAAM,MAAM,MAAM,KAAK,WAAW,KAAK;EAIvC,MAAM,QAAQ,IAAI,YAAY;EAI9B,OAAO;GACL;GACA,QAJA,MAAM,iBAAiB,aAAa,KAAK,MAAM,UAAU,IAAI,EAAG,CAAC,EAAA,CACjE,QAAQ,SAAS,cAAc,MAAM,KAAK,CAG1C;GACA,QAAQ,QAAQ;GAChB,YAAY,QAAQ;GACpB,iBAAiB;IACf,YAAY,QAAQ,cAAc;IAClC,UAAU;IACV,YAAY,QAAQ,cAAc;IAClC,MAAM,QAAQ;GAChB;EACF;CACF;;;;;;;;CASA,MAAc,qBACZ,IACA,KAC0B;EAC1B,MAAM,UAA2B,CAAC;EAClC,KAAA,MAAW,QAAQ,IAAI,OAAO;GAC5B,MAAM,MAAM,KAAK,oBAAoB,IAAI,IAAI;GAC7C,IAAI,OAAO,GAAG;GACd,MAAM,GAAG,OACP,KAAK,gBACL,IAAI,YACJ,CAAC,KACD,IAAI,eACN;GACA,QAAQ,KAAK;IACX,gBAAgB,KAAK;IACrB;IACA,YAAY,IAAI;GAClB,CAAC;EACH;EACA,OAAO;CACT;;;;;;CAOA,MAAc,yBACZ,IACA,KACwB;EACxB,MAAM,GAAG,QACP,IAAI,eACJ,IAAI,YACJ,IAAI,KACJ,IAAI,eACN;EACA,OAAO;GACL,eAAe,IAAI;GACnB,KAAK,IAAI;GACT,YAAY,IAAI;EAClB;CACF;;;;;;;;;;;;;;;;;;;;;;CAuBA,MAAc,WACZ,OAC0B;EAC1B,IAAI,MAAM,OAAO;GACf,MAAM,QAAQ,MAAM;GAGpB,MAAM,WAAW,iBAAiB,CAAA,EAAG;GACrC,MAAM,MAAM,YAEN,MAAM,iBAAiB,gBACrB,KAAK,KAAK,KAAK;IAAE,OAAO,EAAE,IAAI,MAAM;IAAG,OAAO;GAAE,CAAC,CACnD,EAAA,CACA,MAAM,QAAQ,cAAc,KAAK,QAAQ,CAAC,KAAK,OACjD,MAAM,KAAK,KAAK,IAAI,KAAK;GAC7B,IAAI,CAAC,KAAK,MAAM,IAAI,iBAAiB,KAAK;GAC1C,OAAO;EACT;EACA,MAAM,YAAY,MAAM,aAAa;EACrC,IAAI,CAAC,WAQH,MAAM,IAAI,MACR,gFACF;EAEF,MAAM,SAAS,MAAM,KAAK,KAAK,qBAAqB,SAAS;EAC7D,IAAI,CAAC,QAAQ,MAAM,IAAI,2BAA2B,SAAS;EAC3D,OAAO;CACT;AACF;AA+BA,SAAS,eACP,OACA,SACgB;CAChB,iBAAiB,QAAQ,YAAY,sBAAsB;CAC3D,kBAAkB,QAAQ,KAAK,sBAAsB;CACrD,IAAI,CAAC,QAAQ,eACX,MAAM,IAAI,MACR,6IACF;CAEF,MAAM,UAAU,MAAM,MAAM;CAC5B,IAAI,CAAC,SACH,MAAM,IAAI,MACR,mEACF;CAEF,OAAO;EACL;EACA,eAAe,QAAQ;EACvB,KAAK,QAAQ;EACb,YAAY,QAAQ;EACpB,iBAAiB;GACf,YAAY,QAAQ,cAAc;GAClC,UAAU;GACV,YAAY,QAAQ,cAAc;GAClC,MAAM,QAAQ;EAChB;CACF;AACF;AAEA,SAAS,kBAAkB,KAAa,IAAkB;CACxD,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,GAClC,MAAM,IAAI,MAAM,GAAG,GAAE,8CAA+C,IAAG,EAAG;AAE9E;AAEA,SAAS,iBAAiB,YAAoB,IAAkB;CAC9D,IAAI,CAAC,cAAc,OAAO,eAAe,UACvC,MAAM,IAAI,MAAM,GAAG,GAAE,yBAA0B;AAEnD;AASA,eAAe,kBACb,SACuB;CACvB,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MACR,qEACF;CAEF,OAAO,mBAAmB,EAAE,IAAI,QAAQ,GAAG,CAAC;AAC9C;AAKA,eAAsB,wBACpB,SAC4B;CAC5B,OAAO,kBAAkB,OAAO,OAAO;AACzC;;;AC/iBA,IAAM,SAAS,aAAa,EAAE,OAAO,OAAO,CAAC;AA+I7C,eAAsB,qCACpB,SACiD;CACjD,MAAM,EACJ,aACA,0BAA0B,MAC1B,6BAA6B,OAC7B,mBAAmB,UACjB;CAEJ,MAAM,oBACJ,QAAQ,qBAAsB,MAAM,uBAAuB,OAAO;CAEpE,MAAM,YAAkE,CAAC;CAEzE,IAAI,yBAAyB;EAC3B,MAAM,wBACJ,oBAAoB,CAAC;EACvB,MAAM,UAA2B,OAAO,SAAS,aAAa;GAC5D,MAAM,uBACJ,mBACA,SACA,UACA,qBACF;EACF;EACA,YAAY,GAAG,2BAA2B,OAAO;EACjD,UAAU,KAAK;GAAE,SAAS;GAA2B;EAAQ,CAAC;CAChE;CAEA,IAAI,4BAA4B;EAC9B,MAAM,UAA2B,OAAO,SAAS,aAAa;GAC5D,MAAM,0BACJ,mBACA,SACA,QACF;EACF;EACA,YAAY,GAAG,8BAA8B,OAAO;EACpD,UAAU,KAAK;GAAE,SAAS;GAA8B;EAAQ,CAAC;CACnE;CAEA,OAAO;EACL;EACA,UAAU;GACR,KAAA,MAAW,SAAS,WAClB,YAAY,IAAI,MAAM,SAAS,MAAM,OAAO;GAE9C,UAAU,SAAS;EACrB;CACF;AACF;AAEA,eAAe,uBACb,mBACA,SACA,UACA,eACe;CAQf,MAAM,eAAe,iCAAiC,OAAO;CAC7D,IAAI,gBAAgB,CAAC,SAAS;EAC5B,qBACE,2BACA,SACA,UACA,gBAAgB,2BAClB;EACA;CACF;CAQA,IAAI,iBAAiB,CAAC,QAAQ,eAC5B,MAAM,IAAI,MACR,wHACyD,QAAQ,kBAAiB,yHAGpF;CAGF,MAAM,QAAQ;EACZ,IAAI,QAAQ;EACZ,WAAW,QAAQ;EACnB,OAAO,QAAQ;CACjB;CACA,MAAM,cAAc,SAAS,SACzB,oBAAoB,SAAS,WAC7B,KAAA;CAOJ,IAAI,iBAAiB,QAAQ,eAAe;EAC1C,MAAM,cAAc,SAAS,SACzB,oBAAoB,SAAS,WAC7B,KAAA;EACJ,MAAM,kBAAkB,cAAc,OAAO;GAC3C,SAAS;IACP,YAAY,QAAQ;IACpB,KAAK,QAAQ;IACb,MAAM;GACR;GACA,SAAS;IACP,YAAY,QAAQ;IACpB,KAAK,QAAQ;IACb,eAAe,QAAQ;IACvB,MAAM;GACR;EACF,CAAC;EACD;CACF;CAKA,MAAM,kBAAkB,iBAAiB,OAAO;EAC9C,YAAY,QAAQ;EACpB,KAAK,QAAQ;EACb,MAAM;CACR,CAAC;AACH;AAEA,eAAe,0BACb,mBACA,SACA,UACe;CACf,MAAM,kBAAkB,iCAAiC,OAAO;CAChE,IAAI,mBAAmB,CAAC,SAAS;EAC/B,qBACE,8BACA,SACA,UACA,mBAAmB,2BACrB;EACA;CACF;CACA,MAAM,kBAAkB,qBACtB,EAAE,IAAI,QAAQ,kBAAkB,GAChC;EACE,YAAY,QAAQ;EACpB,KAAK,QAAQ;EACb,eAAe,QAAQ;EACvB,MAAM,SAAS,SAAS,oBAAoB,SAAS,WAAW,KAAA;CAClE,CACF;AACF;AAUA,SAAS,iCACP,SACe;CACf,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,OAAO;CAET,IACE,CAAC,QAAQ,qBACT,OAAO,QAAQ,sBAAsB,UAErC,OAAO;CAET,OAAO;AACT;AAWA,SAAS,qBACP,QACA,SACA,UACA,QACM;CACN,OAAO,KACL,kEAAkE,OAAM,mCACnC,OAAM,aAC9B,SAAS,UAAU,YAAW,kBACtC,WAAW,OAAO,YAAY,WAAW,OAAO,KAAK,OAAO,CAAA,CAAE,KAAK,GAAG,IAAI,OAAO,SACxF;AACF;AASA,eAAe,uBACb,SAC4B;CAC5B,IAAI,QAAQ,cASV,OAAO,wBAAwB;EAC7B,cAAc,QAAQ;EACtB,GAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,GAAG,IAAI,CAAC;CACzC,CAAC;CAEH,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MACR,uGACF;CAEF,OAAO,wBAAwB,EAAE,IAAI,QAAQ,GAAG,CAAC;AACnD;;;ACzYO,IAAM,mBAAN,MAAM,iBAAiB;CACpB,YAA4B,YAAiC;EAAjC,KAAA,aAAA;CAAkC;CAAlC;;CAGpC,aAAa,OACX,SAC2B;EAC3B,OAAO,IAAI,iBACT,MAAM,oBAAoB,OAAO,EAAE,IAAI,QAAQ,GAAG,CAAC,CACrD;CACF;;;;;;CAOA,MAAM,OAAO,OAAiD;EAC5D,MAAM,OAAO,MAAM,MAAM,KAAK,KAAK;EACnC,MAAM,OAAO,MAAM,MAAM,KAAK,KAAK;EACnC,IAAI,CAAC,MACH,MAAM,IAAI,2BAA2B,6BAA6B;EACpE,IAAI,CAAC,MACH,MAAM,IAAI,2BAA2B,6BAA6B;EACpE,IAAI,MAAM,KAAK,WAAW,WAAW,IAAI,GACvC,MAAM,IAAI,4BAA4B,IAAI;EAC5C,IAAI;GAGF,OAAO,MAAM,KAAK,WAAW,OAAO;IAClC;IACA;IACA,UAAU,MAAM,UAAU,KAAK,KAAK;IACpC,yBAAyB,MAAM,yBAAyB,KAAK,KAAK;IAClE,UAAU;IACV,aAAa;GACf,CAAC;EACH,SAAS,OAAO;GACd,IAAI,MAAM,KAAK,WAAW,WAAW,IAAI,GACvC,MAAM,IAAI,4BAA4B,IAAI;GAC5C,MAAM;EACR;CACF;;CAGA,MAAM,IAAI,IAAgC;EACxC,MAAM,YAAY,KAAK,MAAM,KAAK,WAAW,IAAI,EAAE,IAAI;EACvD,IAAI,CAAC,WAAW,MAAM,IAAI,uBAAuB,EAAE;EACnD,OAAO;CACT;;CAGA,MAAM,KAAK,UAAwC,CAAC,GAAyB;EAC3E,OAAO,KAAK,WAAW,eAAe,OAAO;CAC/C;;CAGA,MAAM,OAAO,IAAY,MAAkC;EACzD,MAAM,OAAO,MAAM,KAAK,KAAK;EAC7B,IAAI,CAAC,MACH,MAAM,IAAI,2BAA2B,6BAA6B;EACpE,MAAM,YAAY,MAAM,KAAK,IAAI,EAAE;EACnC,UAAU,OAAO;EACjB,MAAM,UAAU,KAAK;EACrB,OAAO;CACT;;CAGA,MAAM,OACJ,IACA,SACoB;EACpB,MAAM,YAAY,MAAM,KAAK,IAAI,EAAE;EACnC,IAAI,QAAQ,aAAa,KAAA,GACvB,UAAU,WAAW,QAAQ,SAAS,KAAK;EAC7C,IAAI,QAAQ,4BAA4B,KAAA,GACtC,UAAU,0BACR,QAAQ,wBAAwB,KAAK,KAAK;EAC9C,MAAM,UAAU,KAAK;EACrB,OAAO;CACT;;CAGA,MAAM,OAAO,IAAgC;EAC3C,OAAO,KAAK,UAAU,IAAI,KAAK;CACjC;;CAGA,MAAM,UAAU,IAAgC;EAC9C,OAAO,KAAK,UAAU,IAAI,IAAI;CAChC;CAEA,MAAc,UAAU,IAAY,UAAuC;EACzE,MAAM,YAAY,MAAM,KAAK,IAAI,EAAE;EACnC,IAAI,UAAU,aAAa,UAAU;GACnC,UAAU,WAAW;GACrB,MAAM,UAAU,KAAK;EACvB;EACA,OAAO;CACT;AACF;AAGA,eAAsB,uBACpB,SAC2B;CAC3B,OAAO,iBAAiB,OAAO,OAAO;AACxC;;;ACxDA,IAAM,eACJ;AAGF,IAAM,gCAAgB,IAAI,IAAI,CAAC,WAAW,aAAa,CAAC;AAcjD,IAAM,uBAAN,MAAM,qBAAqB;CACxB,YAEU,cAEA,MAEA,aAEA,MAChB;EAPgB,KAAA,eAAA;EAEA,KAAA,OAAA;EAEA,KAAA,cAAA;EAEA,KAAA,OAAA;CACf;CAPe;CAEA;CAEA;CAEA;;CAIlB,aAAa,OACX,SAC+B;EAC/B,IAAI,eAAe,QAAQ;EAC3B,IAAI,CAAC,cAAc;GACjB,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MACR,wEACF;GAOF,eAAe,MAAM,mBAAmB,EAAE,IAAI,MAHtB,gBACtB,QAAQ,EACV,EACuD,CAAC;EAC1D;EACA,MAAM,KAAK,aAAa;EAGxB,IAAI,OAAQ,GAAkC,gBAAgB,YAC5D,MAAM,IAAI,MACR,8EACF;EACF,MAAM,CAAC,MAAM,aAAa,QAAQ,MAAM,QAAQ,IAAI;GAClD,wBAAwB,OAAO,EAAE,GAAG,CAAC;GACrC,kCAAkC,OAAO,EAAE,GAAG,CAAC;GAC/C,0BAA0B,OAAO,EAAE,GAAG,CAAC;EACzC,CAAC;EACD,OAAO,IAAI,qBAAqB,cAAc,MAAM,aAAa,IAAI;CACvE;;;;;;;;;;CAWA,MAAM,UAAU,OAAyD;EACvE,MAAM,YAAY,SAAS,MAAM,WAAW,WAAW;EAGvD,MAAM,WAAW,iBAAiB,CAAA,EAAG,YAAY;EACjD,IAAI,MAA8B;EAClC,IAAI,MAAM,OAAO;GACf,MAAM,QAAQ,MAAM;GAMpB,OALc,aAAa,KAAK,KAAK,IACjC,MAAM,iBAAiB,gBACrB,KAAK,KAAK,KAAK;IAAE,OAAO,EAAE,IAAI,MAAM;IAAG,OAAO;GAAE,CAAC,CACnD,IACA,CAAC,EAAA,CACO,MAAM,QAAQ,cAAc,KAAK,QAAQ,CAAC,KAAK;GAC3D,IAAI,CAAC,KAAK,IAAI,MAAM,IAAI,iBAAiB,KAAK;EAChD,OAAA,IAAW,MAAM,WAAW;GAC1B,MAAM,YAAY,MAAM;GAExB,MAAM,UACJ,MAAM,iBAAiB,gBACrB,KAAK,KAAK,KAAK;IACb,OAAO;KAAE;KAAW,QAAQ;IAAS;IACrC,SAAS;GACX,CAAC,CACH,EAAA,CACA,QAAQ,cAAc,cAAc,WAAW,QAAQ,CAAC;GAC1D,MACE,OAAO,MAAM,eAAe,UAAU,YAAY,UAAU,QAAQ,KACpE,OAAO,MACP;GACF,IAAI,CAAC,KAAK,IAAI,MAAM,IAAI,2BAA2B,MAAM,SAAS;EACpE,OACE,MAAM,IAAI,+BACR,gDACF;EAIF,OAAO,KAAK,KAAK,OAAO;GACtB,UAAU,IAAI,YAAY;GAC1B,OAAO,IAAI;GACX;GACA,cAAc;GACd,QAAQ;EACV,CAAC;CACH;;CAGA,MAAM,IAAI,OAA8C;EACtD,IAAI,CAAC,aAAa,KAAK,KAAK,GAAG,OAAO;EACtC,OAAO,KAAK,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC;CACpC;;CAGA,MAAM,gBAAgB,OAAmD;EACvE,IAAI,CAAC,aAAa,KAAK,KAAK,GAAG,OAAO,CAAC;EACvC,OAAO,KAAK,YAAY,UAAU,KAAK;CACzC;;CAGA,MAAM,MAAM,OAAuC;EACjD,OAAO,KAAK,OAAO,OAAO,UAAU,QAAQ;GAC1C,IAAI,IAAI,WAAW,WACjB,MAAM,IAAI,wBAAwB,OAAO,IAAI,QAAQ,OAAO;GAC9D,IAAI,SAAS;EACf,CAAC;CACH;;CAGA,MAAM,OAAO,OAAuC;EAClD,OAAO,KAAK,OAAO,OAAO,WAAW,QAAQ;GAC3C,IAAI,SAAS;EACf,CAAC;CACH;;CAGA,MAAM,OAAO,OAAuC;EAClD,OAAO,KAAK,OAAO,OAAO,WAAW,QAAQ;GAC3C,IAAI,SAAS;EACf,CAAC;CACH;;;;;;;;CASA,MAAM,UAAU,OAAe,WAA2C;EACxE,MAAM,SAAS,SAAS,WAAW,WAAW;EAC9C,OAAO,KAAK,OAAO,OAAO,aAAa,QAAQ;GAE7C,MAAM,YAAY,aAAa,OAAO,IAAI,YAAY,CAAC;GACvD,MAAM,OAAO,aAAa,MAAM;GAChC,IAAI,OAAO,WACT,MAAM,IAAI,+BACR,kCAAkC,eAAe,SAAS,EAAC,eAC7D;GACF,IAAI,YAAY;GAChB,IAAI,YAAY,KAAK,aAAa,MAAM,IAAI,SAAS;EACvD,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;CAsBA,MAAM,iBACJ,OACA,OACiC;EACjC,MAAM,MAAM,SAAS,MAAM,KAAK,KAAK;EACrC,MAAM,cACJ,MAAM,gBAAgB,KAAA,oBAClB,IAAI,KAAK,IACT,IAAI,KAAK,MAAM,WAAW;EAChC,IAAI,OAAO,MAAM,YAAY,QAAQ,CAAC,GACpC,MAAM,IAAI,+BACR,kCACF;EAEF,OAAO,KAAK,cAAc,OAAO,OAAO;GACtC,MAAM,MAAM,MAAM,KAAK,KAAK,IAAI,KAAK;GACrC,IAAI,CAAC,cAAc,IAAI,IAAI,MAAM,GAC/B,MAAM,IAAI,wBAAwB,OAAO,IAAI,QAAQ,UAAU;GAEjE,MAAM,SAAS,aAAa,OAAO,IAAI,SAAS,CAAC;GACjD,MAAM,OAAO,aAAa,OAAO,IAAI,YAAY,CAAC;GAClD,MAAM,WAAW,aAAa,GAAG;GACjC,MAAM,YAAY,KAAK,IAAI,GAAG,SAAS,IAAI;GAC3C,IAAI,WAAW,WACb,MAAM,IAAI,iCACR,OACA,KACA,eAAe,SAAS,CAC1B;GACF,MAAM,QAAQ,OAAO;GACrB,IAAI,eAAe,eAAe,KAAK;GACvC,IAAI,SAAS,SAAS,SAAS,SAAS;GAExC,MAAM,IAAI,KAAK;GAEf,MAAM,aAAa,MAAM,GAAG,YAAY,OAAO;IAC7C,UAAU,IAAI,YAAY;IAC1B;IACA;IACA;GACF,CAAC;GAED,IAAI,WAA4B,CAAC;GACjC,IAAI,WAAiC;GAGrC,MAAM,eAAkB,SACtB,IAAI,WAAW,WAAW,EAAE,UAAU,IAAI,SAAS,GAAG,IAAI,IAAI,KAAK;GACrE,IAAI,MAAM,WAAW,MAAM,SAAS;IAElC,MAAM,aAAa,MAAM,kBAAkB,OAAO,EAChD,cAAc,GAAG,MACnB,CAAC;IACD,MAAM,MAAM;KAAE,IAAI,WAAW;KAAI,OAAO,IAAI;IAAM;IAClD,MAAM,aAAa;IACnB,MAAM,UAAU,MAAM,WAAW;KAC/B,GAAG,MAAM;KACT;KACA;IACF;IACA,MAAM,UAAU,MAAM,WAAW;KAC/B,GAAG,MAAM;KACT;KACA;IACF;IACA,IAAI,WAAW,SACb,CAAC,CAAE,UAAU,YAAa,MAAM,kBAC9B,WAAW,cAAc,KAAK;KAAE;KAAS;IAAQ,CAAC,CACpD;SACF,IAAW,SACT,WAAW,MAAM,kBACf,WAAW,iBAAiB,KAAK,OAAO,CAC1C;SACF,IAAW,SACT,WAAW,MAAM,kBACf,WAAW,qBAAqB,KAAK,OAAO,CAC9C;GAEJ;GACA,OAAO;IAAE;IAAK;IAAY;IAAU;GAAS;EAC/C,CAAC;CACH;;CAGQ,OACN,OACA,QACA,OACwB;EACxB,OAAO,KAAK,cAAc,OAAO,OAAO;GACtC,MAAM,MAAM,MAAM,KAAK,KAAK,IAAI,KAAK;GACrC,IAAI,CAAC,cAAc,IAAI,IAAI,MAAM,GAC/B,MAAM,IAAI,wBAAwB,OAAO,IAAI,QAAQ,MAAM;GAC7D,MAAM,GAAG;GACT,MAAM,IAAI,KAAK;GACf,OAAO;EACT,CAAC;CACH;CAEA,MAAc,KACZ,IACA,OACwB;EACxB,MAAM,MAAM,aAAa,KAAK,KAAK,IAC/B,MAAM,GAAG,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC,IAC/B;EACJ,IAAI,CAAC,KAAK,MAAM,IAAI,2BAA2B,KAAK;EACpD,OAAO;CACT;;;;;CAMQ,cACN,MACY;EACZ,OAAO,0BACL,KAAK,aAAa,gBAAgB,OAAO,UAAU;GACjD,MAAM,KAAK,MAAM;GACjB,MAAM,CAAC,MAAM,eAAe,MAAM,QAAQ,IAAI,CAC5C,wBAAwB,OAAO,EAAE,GAAG,CAAC,GACrC,kCAAkC,OAAO,EAAE,GAAG,CAAC,CACjD,CAAC;GACD,OAAO,KAAK;IAAE;IAAO;IAAM;GAAY,CAAC;EAC1C,CAAC,CACH;CACF;AACF;AAGA,IAAM,wBAAwB;AAE9B,eAAe,oBAAqB,KAAmC;CACrE,KAAA,IAAS,UAAU,IAAK,WACtB,IAAI;EACF,OAAO,MAAM,IAAI;CACnB,SAAS,OAAO;EAEd,IADc,OAAoC,SAEvC,+BACT,WAAW,uBAEX,MAAM;CACV;AAEJ;AAMA,SAAS,SAAS,OAAe,OAAuB;CACtD,MAAM,MAAM,OAAO,KAAK;CACxB,MAAM,SAAS,OAAO,SAAS,GAAG,IAAI,cAAc,GAAG,IAAI;CAC3D,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GACxC,MAAM,IAAI,+BACR,GAAG,MAAK,2CAA4C,OAAO,KAAK,EAAC,GACnE;CACF,IAAI,SAAA,WACF,MAAM,IAAI,+BACR,GAAG,MAAK,mBAAoB,aAAY,QAAS,OAAO,KAAK,EAAC,GAChE;CACF,OAAO;AACT;AAGO,SAAS,2BACd,SAC+B;CAC/B,OAAO,qBAAqB,OAAO,OAAO;AAC5C;;;ACxZO,IAAM,iBAAN,MAAM,eAAe;CAClB,YACW,IACD,MACA,YACA,OAChB;EAJiB,KAAA,KAAA;EACD,KAAA,OAAA;EACA,KAAA,aAAA;EACA,KAAA,QAAA;CACf;CAJgB;CACD;CACA;CACA;;CAIlB,aAAa,OAAO,SAAyD;EAC3E,MAAM,KAAM,MAAM,gBAAgB,QAAQ,EAAE;EAC5C,MAAM,CAAC,MAAM,YAAY,SAAS,MAAM,QAAQ,IAAI;GAClD,0BAA0B,OAAO,EAAE,GAAG,CAAC;GACvC,oBAAoB,OAAO,EAAE,GAAG,CAAC;GACjC,sBAAsB,OAAO,EAAE,GAAG,CAAC;EACrC,CAAC;EACD,OAAO,IAAI,eAAe,IAAI,MAAM,YAAY,KAAK;CACvD;;CAGA,MAAM,KAAK,OAAuC;EAChD,OAAO,KAAK,MAAM,UAAU,KAAK;CACnC;;;;;;CAOA,MAAc,SACZ,SACA,QACe;EAEf,MAAM,2BAAW,IAAI,IAAoB;EACzC,KAAA,MAAW,QAAQ,SACjB,SAAS,IAAI,KAAK,cAAc,SAAS,IAAI,KAAK,WAAW,KAAK,KAAK,CAAC;EAC1E,KAAA,MAAW,SAAS,QAAQ;GAC1B,MAAM,UAAU,OAAO,MAAM,gBAAgB;GAC7C,IAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,GACzC,MAAM,IAAI,2BACR,yCACF;GACF,MAAM,YAAY,MAAM,cACpB,MAAM,KAAK,WAAW,IAAI,MAAM,WAAW,IAC3C;GACJ,IAAI,CAAC,WAAW,MAAM,IAAI,uBAAuB,MAAM,WAAW;GAClE,MAAM,OAAO,SAAS,IAAI,UAAU,EAAY,KAAK;GACrD,IAAI,OAAO,GAAG,SAAS,IAAI,UAAU,IAAc,OAAO,CAAC;QAAA,IAClD,CAAC,UAAU,UAClB,MAAM,IAAI,sBAAsB,UAAU,EAAY;EAC1D;CACF;;;;;;;;;;CAWA,MAAM,eACJ,OACA,QACwB;EACxB,IAAI,CAAC,SAAS,CAAE,MAAM,KAAK,KAAK,IAAI,KAAK,GACvC,MAAM,IAAI,iBAAiB,KAAK;EAElC,MAAM,QAAQ,OACZ,OACA,SACG;GAKH,MAAM,MAAM,MAAM,KAAK,IAAI,KAAK;GAChC,IAAI,CAAC,KAAK,MAAM,IAAI,iBAAiB,KAAK;GAC1C,MAAM,IAAI,KAAK;GAEf,MAAM,UAAU,MAAM,MAAM,UAAU,KAAK;GAC3C,MAAM,KAAK,SAAS,SAAS,MAAM;GACnC,KAAA,MAAW,QAAQ,SAAS,MAAM,KAAK,OAAO;GAC9C,MAAM,QAAuB,CAAC;GAC9B,KAAA,MAAW,CAAC,OAAO,UAAU,OAAO,QAAQ,GAE1C,MAAM,KACJ,MAAM,MAAM,OAAO;IACjB;IACA,aAAa,MAAM;IACnB,UAAU,QAAQ;IAClB,kBAAkB,OAAO,MAAM,gBAAgB;IAC/C,OAAO,MAAM,OAAO,KAAK,KAAK;GAChC,CAAC,CACH;GAEF,OAAO;EACT;EAEA,MAAM,aAAa,KAAK;EAExB,IAAI,OAAO,WAAW,gBAAgB,YACpC,MAAM,IAAI,MACR,wEACF;EACF,OAAO,wBAEH,WAAW,cAAc,OAAO,SAAS;GACvC,MAAM,KAAK;GACX,OAAO,MACL,MAAM,sBAAsB,OAAO,EAAE,GAAG,CAAC,GACzC,MAAM,0BAA0B,OAAO,EAAE,GAAG,CAAC,CAC/C;EACF,CAAC,CACL;CACF;AACF;AAMA,eAAe,kBAAqB,KAAmC;CACrE,KAAA,IAAS,UAAU,IAAK,WACtB,IAAI;EACF,OAAO,MAAM,IAAI;CACnB,SAAS,OAAO;EAEd,IADc,OAAoC,SACrC,+BAA+B,WAAW,GAAG,MAAM;CAClE;AAEJ;AAGA,eAAsB,qBACpB,SACyB;CACzB,OAAO,eAAe,OAAO,OAAO;AACtC"}
1
+ {"version":3,"file":"index.js","names":["sku","trail","lines"],"sources":["../src/__smrt-register__.ts","../src/models/Operation.ts","../src/models/Assembly.ts","../src/collections/AssemblyCollection.ts","../src/models/BillOfMaterials.ts","../src/collections/BillOfMaterialsCollection.ts","../src/models/BomLine.ts","../src/collections/BomLineCollection.ts","../src/collections/OperationCollection.ts","../src/quantity.ts","../src/models/ProductionRun.ts","../src/collections/ProductionRunCollection.ts","../src/models/ProductionRunCompletion.ts","../src/collections/ProductionRunCompletionCollection.ts","../src/models/RoutingStep.ts","../src/collections/RoutingStepCollection.ts","../src/operation-types.ts","../src/production-run-types.ts","../src/tenant-scope.ts","../src/services/AssemblyService.ts","../src/services/structure-walk.ts","../src/services/BomService.ts","../src/services/ProductionService.ts","../src/services/dispatch-handlers.ts","../src/services/OperationService.ts","../src/services/ProductionRunService.ts","../src/services/RoutingService.ts"],"sourcesContent":["// Generated by @happyvertical/smrt-core smrtPlugin at library build time.\n// The package manifest is inlined so self-registration cannot miss when a\n// downstream bundler relocates this module away from dist/manifest.json\n// (https://github.com/happyvertical/smrt/issues/1506,\n// https://github.com/happyvertical/smrt/issues/1507).\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(JSON.parse(\"{\\\"version\\\":\\\"1.0.0\\\",\\\"timestamp\\\":0,\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"packageVersion\\\":\\\"0.57.27\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-manufacturing:BillOfMaterialsCollection\\\":{\\\"name\\\":\\\"billofmaterialscollection\\\",\\\"className\\\":\\\"BillOfMaterialsCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BillOfMaterialsCollection\\\",\\\"collection\\\":\\\"billofmaterialses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/BillOfMaterialsCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByProduct\\\":{\\\"name\\\":\\\"findByProduct\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"productId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BillOfMaterials[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findActiveForProduct\\\":{\\\"name\\\":\\\"findActiveForProduct\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"productId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BillOfMaterials | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"BomStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BillOfMaterials[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"BillOfMaterials\\\",\\\"exportName\\\":\\\"BillOfMaterialsCollection\\\",\\\"collectionExportName\\\":\\\"BillOfMaterialsCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_boms\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"product_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"version\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"effective_date\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'draft',\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"currency\\\\\\\" TEXT DEFAULT 'USD'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"product_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"effective_date\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"draft\\\"},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"currency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"USD\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_boms_product_id_version_idx\\\",\\\"columns\\\":[\\\"product_id\\\",\\\"version\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_boms_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_boms_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"b2691adc\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:BomLineCollection\\\":{\\\"name\\\":\\\"bomlinecollection\\\",\\\"className\\\":\\\"BomLineCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BomLineCollection\\\",\\\"collection\\\":\\\"bomlines\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/BomLineCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByBom\\\":{\\\"name\\\":\\\"findByBom\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"bomId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BomLine[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByComponent\\\":{\\\"name\\\":\\\"findByComponent\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"componentSkuId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<BomLine[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"BomLine\\\",\\\"exportName\\\":\\\"BomLineCollection\\\",\\\"collectionExportName\\\":\\\"BomLineCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_bom_lines\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"component_sku_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"qty_per_unit\\\\\\\" REAL,\\\\n \\\\\\\"uom\\\\\\\" TEXT DEFAULT 'each',\\\\n \\\\\\\"waste_percent\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"component_sku_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"qty_per_unit\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"uom\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"each\\\"},\\\"waste_percent\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_bom_lines_bom_id_component_sku_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"component_sku_id\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_bom_lines_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_bom_lines_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"cfbae7e8\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:OperationCollection\\\":{\\\"name\\\":\\\"operationcollection\\\",\\\"className\\\":\\\"OperationCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:OperationCollection\\\",\\\"collection\\\":\\\"operations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/OperationCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByCode\\\":{\\\"name\\\":\\\"findByCode\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"code\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Operation | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"listOperations\\\":{\\\"name\\\":\\\"listOperations\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"object\\\",\\\"optional\\\":true,\\\"memberTypes\\\":[\\\"boolean\\\"]}],\\\"returnType\\\":\\\"Promise<Operation[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"Operation\\\",\\\"exportName\\\":\\\"OperationCollection\\\",\\\"collectionExportName\\\":\\\"OperationCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_operations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"code\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"name\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"is_active\\\\\\\" BOOLEAN DEFAULT TRUE,\\\\n \\\\\\\"required_qualification_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"is_active\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":true},\\\"required_qualification_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_operations_code_tenant_id_idx\\\",\\\"columns\\\":[\\\"code\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_operations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_operations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_operations_required_qualification_id_idx\\\",\\\"columns\\\":[\\\"required_qualification_id\\\"]}],\\\"version\\\":\\\"0b3134d6\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRunCollection\\\":{\\\"name\\\":\\\"productionruncollection\\\",\\\"className\\\":\\\"ProductionRunCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRunCollection\\\",\\\"collection\\\":\\\"productionruns\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/ProductionRunCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByBom\\\":{\\\"name\\\":\\\"findByBom\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"bomId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<ProductionRun[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByStatus\\\":{\\\"name\\\":\\\"findByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"status\\\",\\\"type\\\":\\\"ProductionRunStatus\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<ProductionRun[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"ProductionRun\\\",\\\"exportName\\\":\\\"ProductionRunCollection\\\",\\\"collectionExportName\\\":\\\"ProductionRunCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_runs\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"target_qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_qty\\\\\\\" REAL,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'planned'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"target_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"planned\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_runs_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_runs_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_runs_bom_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\"]}],\\\"version\\\":\\\"2ca8147d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletionCollection\\\":{\\\"name\\\":\\\"productionruncompletioncollection\\\",\\\"className\\\":\\\"ProductionRunCompletionCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletionCollection\\\",\\\"collection\\\":\\\"productionruncompletions\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/ProductionRunCompletionCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByRun\\\":{\\\"name\\\":\\\"findByRun\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"runId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<ProductionRunCompletion[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"ProductionRunCompletion\\\",\\\"exportName\\\":\\\"ProductionRunCompletionCollection\\\",\\\"collectionExportName\\\":\\\"ProductionRunCompletionCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_run_completions\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"run_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_at\\\\\\\" TIMESTAMP\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"run_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_production_runs\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_run_completions_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_run_completions_tenant_ea7a0d0125_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_run_completions_run_id_idx\\\",\\\"columns\\\":[\\\"run_id\\\"]}],\\\"version\\\":\\\"c940de6c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:RoutingStepCollection\\\":{\\\"name\\\":\\\"routingstepcollection\\\",\\\"className\\\":\\\"RoutingStepCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:RoutingStepCollection\\\",\\\"collection\\\":\\\"routingsteps\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/collections/RoutingStepCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByBom\\\":{\\\"name\\\":\\\"findByBom\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"bomId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<RoutingStep[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByOperation\\\":{\\\"name\\\":\\\"findByOperation\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"operationId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<RoutingStep[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"RoutingStep\\\",\\\"exportName\\\":\\\"RoutingStepCollection\\\",\\\"collectionExportName\\\":\\\"RoutingStepCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_routing_steps\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"operation_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"sequence\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"estimated_minutes\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"operation_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_operations\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"sequence\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"estimated_minutes\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_routing_steps_bom_id_sequence_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"sequence\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_routing_steps_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_operation_id_idx\\\",\\\"columns\\\":[\\\"operation_id\\\"]}],\\\"version\\\":\\\"41ac3c66\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:Assembly\\\":{\\\"name\\\":\\\"assembly\\\",\\\"className\\\":\\\"Assembly\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:Assembly\\\",\\\"collection\\\":\\\"products\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/Assembly.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"generated\\\":true,\\\"source\\\":\\\"tenantScoped_decorator\\\",\\\"sqlType\\\":\\\"UUID\\\",\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"autoFilter\\\":true,\\\"autoPopulate\\\":true,\\\"allowSuperAdminBypass\\\":false}}},\\\"productType\\\":{\\\"type\\\":\\\"json\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The product name, as customers see it.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The product name, as customers see it.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"A few words on what the product is and who it is for.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A few words on what the product is and who it is for.\\\"}},\\\"category\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"manufacturer\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"model\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"price\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":0,\\\"description\\\":\\\"The price in minor units (cents), for example 1999 for 19.99.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The price in minor units (cents), for example 1999 for 19.99.\\\"}},\\\"defaultSalesTaxCodeId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-commerce:TaxCode\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The tax code normally used when this product is sold.\\\"}},\\\"defaultPurchaseTaxCodeId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-commerce:TaxCode\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The tax code normally used when this product is purchased.\\\"}},\\\"inStock\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":true,\\\"default\\\":true},\\\"specifications\\\":{\\\"type\\\":\\\"json\\\",\\\"required\\\":false,\\\"default\\\":{}},\\\"tags\\\":{\\\"type\\\":\\\"json\\\",\\\"required\\\":false,\\\"default\\\":[]},\\\"estimatedLabourMinutes\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"defaultOperationId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Operation\\\",\\\"_meta\\\":{\\\"nullable\\\":true,\\\"onDelete\\\":\\\"RESTRICT\\\"}},\\\"partReference\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{\\\"getSpecification\\\":{\\\"name\\\":\\\"getSpecification\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"key\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"updateSpecification\\\":{\\\"name\\\":\\\"updateSpecification\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"key\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"value\\\",\\\"type\\\":\\\"unknown\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getAssets\\\":{\\\"name\\\":\\\"getAssets\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"relationship\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"addAsset\\\":{\\\"name\\\":\\\"addAsset\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"asset\\\",\\\"type\\\":\\\"Asset\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"relationship\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":true,\\\"default\\\":\\\"attachment\\\"},{\\\"name\\\":\\\"sortOrder\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"removeAsset\\\":{\\\"name\\\":\\\"removeAsset\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"relationship\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"tableName\\\":\\\"products\\\",\\\"tableStrategy\\\":\\\"sti\\\"},\\\"extends\\\":\\\"Product\\\",\\\"extendsQualified\\\":\\\"@happyvertical/smrt-products:Product\\\",\\\"exportName\\\":\\\"Assembly\\\",\\\"collectionExportName\\\":\\\"AssemblyCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"price\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"},{\\\"field\\\":\\\"inStock\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"boolean\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"products\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"products\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"product_type\\\\\\\" TEXT,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"category\\\\\\\" TEXT,\\\\n \\\\\\\"manufacturer\\\\\\\" TEXT,\\\\n \\\\\\\"model\\\\\\\" TEXT,\\\\n \\\\\\\"price\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"default_sales_tax_code_id\\\\\\\" UUID,\\\\n \\\\\\\"default_purchase_tax_code_id\\\\\\\" UUID,\\\\n \\\\\\\"in_stock\\\\\\\" BOOLEAN DEFAULT TRUE,\\\\n \\\\\\\"specifications\\\\\\\" JSON DEFAULT '{}',\\\\n \\\\\\\"tags\\\\\\\" JSON DEFAULT '[]',\\\\n \\\\\\\"estimated_labour_minutes\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"default_operation_id\\\\\\\" UUID,\\\\n \\\\\\\"part_reference\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"product_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"manufacturer\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"model\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"price\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"default_sales_tax_code_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"default_purchase_tax_code_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"in_stock\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"default\\\":true},\\\"specifications\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false,\\\"default\\\":{}},\\\"tags\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false,\\\"default\\\":[]},\\\"estimated_labour_minutes\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":0},\\\"default_operation_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_operations\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"part_reference\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"products_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"products_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"products_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"products_default_sales_tax_code_id_idx\\\",\\\"columns\\\":[\\\"default_sales_tax_code_id\\\"]},{\\\"name\\\":\\\"products_default_purchase_tax_code_id_idx\\\",\\\"columns\\\":[\\\"default_purchase_tax_code_id\\\"]},{\\\"name\\\":\\\"products_default_operation_id_idx\\\",\\\"columns\\\":[\\\"default_operation_id\\\"]}],\\\"version\\\":\\\"e1a54866\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:BillOfMaterials\\\":{\\\"name\\\":\\\"billofmaterials\\\",\\\"className\\\":\\\"BillOfMaterials\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BillOfMaterials\\\",\\\"collection\\\":\\\"billofmaterialses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/BillOfMaterials.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"productId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"version\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":1,\\\"_meta\\\":{\\\"required\\\":true}},\\\"effectiveDate\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"draft\\\",\\\"_meta\\\":{\\\"required\\\":true},\\\"enum\\\":[\\\"draft\\\",\\\"active\\\",\\\"superseded\\\"]},\\\"notes\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"currency\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"USD\\\"}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"conflictColumns\\\":[\\\"product_id\\\",\\\"version\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":true,\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"BillOfMaterials\\\",\\\"collectionExportName\\\":\\\"BillOfMaterialsCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"productId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"version\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"},{\\\"field\\\":\\\"status\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_boms\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_boms\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"product_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"version\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"effective_date\\\\\\\" TIMESTAMP,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'draft',\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"currency\\\\\\\" TEXT DEFAULT 'USD'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"product_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"effective_date\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"draft\\\"},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"currency\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"USD\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_boms_product_id_version_idx\\\",\\\"columns\\\":[\\\"product_id\\\",\\\"version\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_boms_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_boms_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"b2691adc\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:BomLine\\\":{\\\"name\\\":\\\"bomline\\\",\\\"className\\\":\\\"BomLine\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:BomLine\\\",\\\"collection\\\":\\\"bomlines\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/BomLine.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"bomId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"componentSkuId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"qtyPerUnit\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"uom\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"each\\\"},\\\"wastePercent\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"notes\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{\\\"effectiveQtyPerUnit\\\":{\\\"name\\\":\\\"effectiveQtyPerUnit\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"number\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"conflictColumns\\\":[\\\"bom_id\\\",\\\"component_sku_id\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"BomLine\\\",\\\"collectionExportName\\\":\\\"BomLineCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"bomId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"componentSkuId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_bom_lines\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_bom_lines\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"component_sku_id\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"qty_per_unit\\\\\\\" REAL,\\\\n \\\\\\\"uom\\\\\\\" TEXT DEFAULT 'each',\\\\n \\\\\\\"waste_percent\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"component_sku_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"qty_per_unit\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"uom\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"each\\\"},\\\"waste_percent\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_bom_lines_bom_id_component_sku_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"component_sku_id\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_bom_lines_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_bom_lines_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]}],\\\"version\\\":\\\"cfbae7e8\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:Operation\\\":{\\\"name\\\":\\\"operation\\\",\\\"className\\\":\\\"Operation\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:Operation\\\",\\\"collection\\\":\\\"operations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/Operation.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"code\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"category\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"},\\\"isActive\\\":{\\\"type\\\":\\\"boolean\\\",\\\"required\\\":false,\\\"default\\\":true},\\\"requiredQualificationId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-human-resources:Qualification\\\",\\\"_meta\\\":{\\\"nullable\\\":true}}},\\\"methods\\\":{\\\"delete\\\":{\\\"name\\\":\\\"delete\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"conflictColumns\\\":[\\\"code\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"Operation\\\",\\\"collectionExportName\\\":\\\"OperationCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"code\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"name\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_operations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_operations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"code\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"name\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"category\\\\\\\" TEXT DEFAULT '',\\\\n \\\\\\\"is_active\\\\\\\" BOOLEAN DEFAULT TRUE,\\\\n \\\\\\\"required_qualification_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"code\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"category\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"is_active\\\":{\\\"type\\\":\\\"BOOLEAN\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":true},\\\"required_qualification_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_operations_code_tenant_id_idx\\\",\\\"columns\\\":[\\\"code\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_operations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_operations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_operations_required_qualification_id_idx\\\",\\\"columns\\\":[\\\"required_qualification_id\\\"]}],\\\"version\\\":\\\"0b3134d6\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRun\\\":{\\\"name\\\":\\\"productionrun\\\",\\\"className\\\":\\\"ProductionRun\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRun\\\",\\\"collection\\\":\\\"productionruns\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/ProductionRun.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"bomId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"BillOfMaterials\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"RESTRICT\\\"}},\\\"targetQty\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"completedQty\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"status\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"planned\\\",\\\"_meta\\\":{\\\"required\\\":true},\\\"enum\\\":[\\\"planned\\\",\\\"in_progress\\\",\\\"done\\\",\\\"cancelled\\\"]}},\\\"methods\\\":{\\\"remainingQty\\\":{\\\"name\\\":\\\"remainingQty\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"number\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"ProductionRun\\\",\\\"collectionExportName\\\":\\\"ProductionRunCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"bomId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"status\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_runs\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_runs\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"target_qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_qty\\\\\\\" REAL,\\\\n \\\\\\\"status\\\\\\\" TEXT NOT NULL DEFAULT 'planned'\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"target_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"status\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"planned\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_runs_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_runs_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_runs_bom_id_idx\\\",\\\"columns\\\":[\\\"bom_id\\\"]}],\\\"version\\\":\\\"2ca8147d\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletion\\\":{\\\"name\\\":\\\"productionruncompletion\\\",\\\"className\\\":\\\"ProductionRunCompletion\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:ProductionRunCompletion\\\",\\\"collection\\\":\\\"productionruncompletions\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/ProductionRunCompletion.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"runId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"ProductionRun\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"CASCADE\\\"}},\\\"qty\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"completedAt\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"ProductionRunCompletion\\\",\\\"collectionExportName\\\":\\\"ProductionRunCompletionCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"runId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_production_run_completions\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_production_run_completions\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"run_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"qty\\\\\\\" REAL,\\\\n \\\\\\\"completed_at\\\\\\\" TIMESTAMP\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"run_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_production_runs\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"qty\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"completed_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_production_run_completions_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_production_run_completions_tenant_ea7a0d0125_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_production_run_completions_run_id_idx\\\",\\\"columns\\\":[\\\"run_id\\\"]}],\\\"version\\\":\\\"c940de6c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"},\\\"@happyvertical/smrt-manufacturing:RoutingStep\\\":{\\\"name\\\":\\\"routingstep\\\",\\\"className\\\":\\\"RoutingStep\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-manufacturing:RoutingStep\\\",\\\"collection\\\":\\\"routingsteps\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/manufacturing/src/models/RoutingStep.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-manufacturing\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"bomId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"BillOfMaterials\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"CASCADE\\\"}},\\\"operationId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"Operation\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"RESTRICT\\\"}},\\\"sequence\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":1,\\\"_meta\\\":{\\\"required\\\":true}},\\\"estimatedMinutes\\\":{\\\"type\\\":\\\"decimal\\\",\\\"required\\\":false,\\\"_meta\\\":{}},\\\"notes\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"\\\"}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"conflictColumns\\\":[\\\"bom_id\\\",\\\"sequence\\\",\\\"tenant_id\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"RoutingStep\\\",\\\"collectionExportName\\\":\\\"RoutingStepCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"bomId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"operationId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"sequence\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"manufacturing_routing_steps\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"manufacturing_routing_steps\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"bom_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"operation_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"sequence\\\\\\\" INTEGER NOT NULL DEFAULT 1,\\\\n \\\\\\\"estimated_minutes\\\\\\\" REAL,\\\\n \\\\\\\"notes\\\\\\\" TEXT DEFAULT ''\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"bom_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_boms\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"operation_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"manufacturing_operations\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"RESTRICT\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"sequence\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":1},\\\"estimated_minutes\\\":{\\\"type\\\":\\\"REAL\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"notes\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"manufacturing_routing_steps_bom_id_sequence_idx\\\",\\\"columns\\\":[\\\"bom_id\\\",\\\"sequence\\\",\\\"tenant_id\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"manufacturing_routing_steps_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"manufacturing_routing_steps_operation_id_idx\\\",\\\"columns\\\":[\\\"operation_id\\\"]}],\\\"version\\\":\\\"41ac3c66\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-manufacturing\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\",\\\"@happyvertical/smrt-inventory\\\",\\\"@happyvertical/smrt-products\\\",\\\"@happyvertical/smrt-tenancy\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3616\\\",\\\"#3624\\\",\\\"#3626\\\"],\\\"via\\\":[\\\"@happyvertical/smrt-products\\\",\\\"@happyvertical/smrt-tenancy\\\"]},\\\"stampsConstructors\\\":true}\"));\n","/**\n * Operation — one kind of work done to build something: cut, weld, assemble,\n * inspect, bake, pack. A managed list per tenant that routing steps point at.\n *\n * Retiring an operation (`isActive = false`) keeps it on every routing and\n * history that already names it and takes it out of pickers; an operation is\n * never deleted. Time recorded against an operation is not modelled here — it\n * stays in `@happyvertical/smrt-timesheets` or the application.\n *\n * Strictly industry-neutral.\n *\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Options accepted by the {@link Operation} constructor.\n */\nexport interface OperationOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n code?: string;\n name?: string;\n category?: string;\n isActive?: boolean;\n requiredQualificationId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_operations',\n // Natural key: one operation per code per tenant.\n conflictColumns: ['code', 'tenant_id'],\n // Writes go through OperationService, which rejects a duplicate code and\n // keeps the code fixed; generated create/update would upsert or rename.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class Operation extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the operation is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Short stable code, unique per tenant (`CUT`, `WELD`). Treated as the\n * operation's identity by {@link OperationService}: it is set when the\n * operation is defined and is not changed afterwards; rename the `name`.\n */\n @field({ required: true })\n code: string = '';\n\n /** Human-readable name shown in lists and pickers. */\n @field({ required: true })\n name: string = '';\n\n /**\n * Free-text grouping for lists (`fabrication`, `finishing`, `quality`).\n * Empty when the operation is not grouped.\n */\n category: string = '';\n\n /**\n * `false` once the operation is retired. A retired operation stays on the\n * routings and history that name it and is left out of pickers.\n */\n isActive: boolean = true;\n\n /**\n * Optional id of the qualification definition a worker needs to start this\n * operation: a `Qualification` from `@happyvertical/smrt-human-resources`.\n * Declared as a cross-package reference (metadata only: this package takes no\n * dependency on it and does not validate the id); an application reads it to\n * decide who may start the operation. `null` when none is required.\n */\n @crossPackageRef('@happyvertical/smrt-human-resources:Qualification', {\n nullable: true,\n })\n requiredQualificationId: string | null = null;\n\n constructor(options: OperationOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.code !== undefined) this.code = options.code;\n if (options.name !== undefined) this.name = options.name;\n if (options.category !== undefined) this.category = options.category;\n if (options.isActive !== undefined) this.isActive = options.isActive;\n if (options.requiredQualificationId !== undefined)\n this.requiredQualificationId = options.requiredQualificationId;\n }\n\n /** Operations are retired, never deleted, so history keeps resolving. */\n override async delete(): Promise<void> {\n throw new Error(\n `Operation ${this.code || this.id} cannot be deleted; retire it instead.`,\n );\n }\n}\n","/**\n * Assembly — the product that is made, the counterpart to `Material` (the\n * product that is consumed).\n *\n * A single-table-inheritance subtype of `@happyvertical/smrt-products`\n * `Product`: assembly rows live in the shared `products` table with\n * `productType: 'assembly'` and `_meta_type:\n * '@happyvertical/smrt-manufacturing:Assembly'`. Its own fields are\n * ordinary columns that this package adds to that table.\n *\n * Everything else comes from a neighbour and is deliberately not repeated\n * here:\n *\n * - name, description, price, category and tags from `Product`;\n * - part number, barcode and stock from its `Sku` (`smrt-products`) and the\n * stock levels in `smrt-inventory`;\n * - what it is made of from its {@link BillOfMaterials} and {@link BomLine}s;\n * - specs, plans and drawings from `ProductAsset` links.\n *\n * **Nesting lives in the bill, not on the assembly.** A sub-assembly is a\n * bill line whose component SKU belongs to another `Assembly`; there is no\n * children list to keep in sync. Use {@link AssemblyService.resolveComponent}\n * to tell a sub-assembly from a material or a bought item.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport {\n Product,\n type ProductOptions,\n ProductType,\n} from '@happyvertical/smrt-products/models';\nimport { TenantScoped } from '@happyvertical/smrt-tenancy';\nimport { Operation } from './Operation.js';\n\n/**\n * Options accepted by the {@link Assembly} constructor.\n */\nexport interface AssemblyOptions extends ProductOptions {\n estimatedLabourMinutes?: number;\n defaultOperationId?: string | null;\n partReference?: string;\n}\n\n// `@TenantScoped` and the generation config are registered per concrete\n// class and are not inherited from the STI parent (see `Material`): repeat\n// both so assembly reads are tenant-filtered and the REST/MCP surface\n// matches `Product` (CRUD without delete over REST, read-only over MCP)\n// instead of falling open.\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: {\n include: ['list', 'get', 'create', 'update'], // no delete, matches Product\n },\n mcp: {\n include: ['list', 'get'], // read-only AI tools, matches Product\n },\n cli: { skipApiCheck: true },\n})\nexport class Assembly extends Product { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n override productType: ProductType = ProductType.ASSEMBLY;\n\n /**\n * Labour to build one unit, in whole minutes. A planning estimate until\n * routing steps (smrt#3445) provide labour per operation; `0` means not\n * estimated.\n */\n @field()\n estimatedLabourMinutes: number = 0;\n\n /**\n * The {@link Operation} that work on this assembly is booked to by\n * default, or `null` for none. `RESTRICT`, like `RoutingStep.operationId`:\n * operations are retired, never deleted.\n */\n @foreignKey(Operation, { nullable: true, onDelete: 'RESTRICT' })\n defaultOperationId: string | null = null;\n\n /**\n * The organization's own drawing or part number for this assembly,\n * alongside (not instead of) the SKU code.\n */\n @field()\n partReference: string = '';\n\n constructor(options: AssemblyOptions = {}) {\n super(options);\n if (options.estimatedLabourMinutes !== undefined)\n this.estimatedLabourMinutes = options.estimatedLabourMinutes;\n if (options.defaultOperationId !== undefined)\n this.defaultOperationId = options.defaultOperationId;\n if (options.partReference !== undefined)\n this.partReference = options.partReference;\n }\n\n /**\n * Refuses a labour estimate that is not a whole number of minutes of zero\n * or more, on every save (generated REST and collection writes included).\n */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n const minutes = this.estimatedLabourMinutes;\n if (!Number.isSafeInteger(minutes) || minutes < 0) {\n throw ValidationError.invalidValue(\n 'estimatedLabourMinutes',\n minutes,\n 'a whole number of minutes, zero or more',\n );\n }\n }\n}\n","/**\n * AssemblyCollection — collection for {@link Assembly} rows.\n *\n * Assemblies share the `products` table with every other `Product` subtype;\n * the STI framework filters this collection to assembly rows by\n * `_meta_type`, so `list()` and `get()` only ever return assemblies.\n *\n * @packageDocumentation\n */\n\nimport { ProductCollection } from '@happyvertical/smrt-products/collections';\nimport { Assembly } from '../models/Assembly.js';\n\nexport class AssemblyCollection extends ProductCollection {\n static override readonly _itemClass = Assembly;\n}\n","/**\n * BillOfMaterials — recipe describing how to produce a finished product.\n *\n * A BOM enumerates the component SKUs (raw materials, trims, sub-assemblies)\n * that must be consumed to produce one unit of a given parent product. The\n * same product may have many BOMs — alternate revisions, alternate recipes,\n * region-specific variations — distinguished by `version` and lifecycle\n * `status`.\n *\n * Strictly industry-neutral: the same model serves apparel, furniture,\n * automotive, CPG, electronics, food production, custom hardware, and any\n * other vertical that builds finished goods from components.\n *\n * Cross-package references (`productId`) are plain string ids — never\n * `@foreignKey()` — so the schema does not couple to the catalog package's\n * table layout. The product is usually an `Assembly`.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { BomStatus } from '../types.js';\n\n/**\n * Options accepted by the {@link BillOfMaterials} constructor.\n */\nexport interface BillOfMaterialsOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n productId?: string;\n version?: number;\n effectiveDate?: Date | string | null;\n status?: BomStatus;\n notes?: string;\n currency?: string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_boms',\n // Natural key: one BOM per (product, version) per tenant. Re-saving the\n // same combination is an upsert rather than a UNIQUE violation.\n conflictColumns: ['product_id', 'version', 'tenant_id'],\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class BillOfMaterials extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the BOM is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Plain string reference to the upstream product this BOM produces.\n * Typically a `Product.id` (or any STI subtype thereof) from\n * `@happyvertical/smrt-products`, usually an `Assembly`. Cross-package\n * id, intentionally not a `@foreignKey()`.\n */\n @field({ required: true })\n productId: string = '';\n\n /**\n * Monotonically increasing revision number scoped per `productId`. Together\n * with `tenantId` and `productId` this forms the natural key — so\n * `version` is an integer and `0` is reserved by convention for unsaved\n * scratch rows.\n */\n @field({ required: true })\n version: number = 1;\n\n /**\n * Date from which this BOM is intended to take effect for new production\n * runs. Optional — older BOMs imported from legacy systems may have no\n * effective date on file.\n */\n effectiveDate: Date | null = null;\n\n /**\n * Lifecycle state — `draft`, `active`, or `superseded`. See\n * {@link BomStatus} for the semantics.\n *\n * Most queries that pick \"the BOM to use right now\" should filter for\n * `status: 'active'`.\n */\n @field({ required: true })\n status: BomStatus = 'draft';\n\n /**\n * Optional free-form notes shown in admin UIs (revision reason, source\n * document reference, sign-off chain, etc.).\n */\n notes: string = '';\n\n /**\n * ISO 4217 currency code for cost rollups. Mostly a hint for the rollup\n * service — defaults to `'USD'` so older imports without a currency\n * still produce sensible numbers.\n */\n currency: string = 'USD';\n\n constructor(options: BillOfMaterialsOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.productId !== undefined) this.productId = options.productId;\n if (options.version !== undefined) this.version = options.version;\n if (options.effectiveDate !== undefined) {\n this.effectiveDate =\n options.effectiveDate === null\n ? null\n : options.effectiveDate instanceof Date\n ? options.effectiveDate\n : new Date(options.effectiveDate);\n }\n if (options.status !== undefined) this.status = options.status;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.currency !== undefined) this.currency = options.currency;\n }\n\n /**\n * Refuses saving the bill as `active` when one of its lines would make\n * its product contain itself, directly or through sub-assemblies' active\n * bills, with a `BomCycleError` naming the path. Draft and superseded\n * bills are not part of the structure and are not checked here (their\n * lines are checked as they are saved).\n */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (this.status !== 'active') return;\n // Loaded lazily: the service imports this model through its collections.\n const { AssemblyService } = await import('../services/AssemblyService.js');\n const service = await AssemblyService.create({ db: this.db });\n await service.assertBillAcyclic(this);\n }\n}\n","/**\n * BillOfMaterialsCollection — query helpers for {@link BillOfMaterials} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomStatus } from '../types.js';\n\nexport class BillOfMaterialsCollection extends SmrtCollection<BillOfMaterials> {\n static readonly _itemClass = BillOfMaterials;\n\n /**\n * Return every BOM for the given upstream product id, newest version\n * first. Useful for \"show me all revisions of this product's recipe\"\n * admin screens.\n */\n async findByProduct(productId: string): Promise<BillOfMaterials[]> {\n return this.list({ where: { productId }, orderBy: 'version DESC' });\n }\n\n /**\n * Return the currently `active` BOM for the given product, or `null` if\n * none has been activated yet. When multiple `active` rows exist (which\n * should not happen but can arise during partial migrations), the\n * highest-version row wins.\n */\n async findActiveForProduct(\n productId: string,\n ): Promise<BillOfMaterials | null> {\n const matches = await this.list({\n where: { productId, status: 'active' },\n orderBy: 'version DESC',\n limit: 1,\n });\n return matches[0] ?? null;\n }\n\n /**\n * Return every BOM with the given lifecycle status across all products.\n * Mostly useful for admin dashboards (e.g. \"show every draft BOM\").\n */\n async findByStatus(status: BomStatus): Promise<BillOfMaterials[]> {\n return this.list({ where: { status }, orderBy: 'productId ASC' });\n }\n}\n","/**\n * BomLine — one component on a {@link BillOfMaterials}.\n *\n * Each line declares the component SKU required to produce one unit of the\n * parent product, the quantity-per-unit, the unit of measure (open-ended\n * string — `yards`, `each`, `grams`, `kg`, `m`, anything else the\n * application needs), and an optional `wastePercent` so cost rollups and\n * requirements explosions can account for offcuts, setup losses, and other\n * material consumed but not incorporated into a finished unit.\n *\n * Cross-package references (`componentSkuId`) are plain string ids — never\n * `@foreignKey()`. The `Sku` model lives in `@happyvertical/smrt-products`\n * and `@happyvertical/smrt-inventory` holds the stock levels and movements\n * addressed by the id. When the SKU belongs to an `Assembly`, the line is a\n * sub-assembly: `AssemblyService.resolveComponent` says which.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Options accepted by the {@link BomLine} constructor.\n */\nexport interface BomLineOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n bomId?: string;\n componentSkuId?: string;\n qtyPerUnit?: number;\n uom?: string;\n wastePercent?: number;\n notes?: string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_bom_lines',\n // Natural key: one row per `(bom, component)` per tenant. Re-saving the\n // same combination updates the row in place rather than violating the\n // UNIQUE constraint. Callers that want to declare two distinct lines for\n // the same component (e.g. \"main\" vs \"trim\" passes) should use distinct\n // component SKUs or model the variants in inventory.\n conflictColumns: ['bom_id', 'component_sku_id', 'tenant_id'],\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class BomLine extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the line is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Plain string reference to the parent {@link BillOfMaterials}. Within\n * this package this is the only \"structural\" foreign key — but it is\n * still stored as a plain string to keep with the package's\n * cross-reference convention and to avoid coupling the schema generator\n * to a particular load order.\n */\n @field({ required: true })\n bomId: string = '';\n\n /**\n * Plain string reference to the component SKU id (raw material, trim,\n * sub-assembly). The `Sku` model itself lives in\n * `@happyvertical/smrt-products`; `@happyvertical/smrt-inventory` uses\n * this id to track stock levels and movements. Cross-package id;\n * never `@foreignKey()`.\n */\n @field({ required: true })\n componentSkuId: string = '';\n\n /**\n * Quantity of the component required per unit of finished product, in\n * the unit declared by {@link uom}. Decimal so fractional usage like\n * `1.75 yards` or `12.5 grams` round-trips correctly.\n */\n @field({ type: 'decimal' })\n qtyPerUnit: number = 0.0;\n\n /**\n * Unit of measure for {@link qtyPerUnit}. Open-ended string so the\n * framework does not constrain the vocabulary — `'yards'`, `'each'`,\n * `'grams'`, `'kg'`, `'m'`, `'litres'`, `'sq_ft'`, anything.\n *\n * Conventions follow the upstream `Material.uom` value when applicable.\n */\n uom: string = 'each';\n\n /**\n * Expected percentage waste on this line. Adds to the effective quantity\n * consumed during cost rollups and requirements explosions so a cutting\n * yield of 10% surfaces in the total cost without callers having to\n * pre-inflate `qtyPerUnit`. Stored as a percent (`10` means 10%), not a\n * fraction (`0.10`).\n */\n @field({ type: 'decimal' })\n wastePercent: number = 0.0;\n\n /**\n * Optional free-form notes — e.g. \"primary structural component\",\n * \"60-second cure\", or \"matched batch only\".\n */\n notes: string = '';\n\n constructor(options: BomLineOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.bomId !== undefined) this.bomId = options.bomId;\n if (options.componentSkuId !== undefined)\n this.componentSkuId = options.componentSkuId;\n if (options.qtyPerUnit !== undefined) this.qtyPerUnit = options.qtyPerUnit;\n if (options.uom !== undefined) this.uom = options.uom;\n if (options.wastePercent !== undefined)\n this.wastePercent = options.wastePercent;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Effective quantity consumed per produced unit, including waste. Used\n * by {@link BomService} for cost rollups and requirements explosions.\n *\n * `effectiveQty = qtyPerUnit * (1 + wastePercent / 100)`\n */\n effectiveQtyPerUnit(): number {\n const qty = Number(this.qtyPerUnit ?? 0);\n const waste = Number(this.wastePercent ?? 0);\n return qty * (1 + waste / 100);\n }\n\n /**\n * Refuses a line that would make its bill's product contain itself,\n * directly or through sub-assemblies' active bills, with a\n * `BomCycleError` naming the path. Runs on every save, so the generated\n * REST `create`/`update` routes refuse it too.\n */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n // Loaded lazily: the service imports this model through its collections.\n const { AssemblyService } = await import('../services/AssemblyService.js');\n const service = await AssemblyService.create({ db: this.db });\n await service.assertLineAcyclic(this);\n }\n}\n","/**\n * BomLineCollection — query helpers for {@link BomLine} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { BomLine } from '../models/BomLine.js';\n\nexport class BomLineCollection extends SmrtCollection<BomLine> {\n static readonly _itemClass = BomLine;\n\n /**\n * Return every line that belongs to the given BOM. Caller-stable order\n * helps make cost rollups and requirements explosions deterministic.\n */\n async findByBom(bomId: string): Promise<BomLine[]> {\n return this.list({ where: { bomId }, orderBy: 'componentSkuId ASC' });\n }\n\n /**\n * Return every line that references the given component SKU across all\n * BOMs. Useful for \"where is this material used?\" admin screens and for\n * cost-change impact analysis (a price hike on a raw material affects\n * every BOM in the result).\n */\n async findByComponent(componentSkuId: string): Promise<BomLine[]> {\n return this.list({ where: { componentSkuId }, orderBy: 'bomId ASC' });\n }\n}\n","/**\n * OperationCollection — query helpers for {@link Operation} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Operation } from '../models/Operation.js';\n\nexport class OperationCollection extends SmrtCollection<Operation> {\n static readonly _itemClass = Operation;\n\n /** Return the operation with the given code, or `null`. */\n async findByCode(code: string): Promise<Operation | null> {\n const matches = await this.list({ where: { code }, limit: 1 });\n return matches[0] ?? null;\n }\n\n /**\n * Return operations ordered by category then code. Retired operations are\n * left out unless `includeRetired` is set, so the default is a picker list.\n */\n async listOperations(\n options: { includeRetired?: boolean } = {},\n ): Promise<Operation[]> {\n return this.list({\n where: options.includeRetired ? {} : { isActive: true },\n orderBy: ['category ASC', 'code ASC'],\n });\n }\n}\n","/**\n * Quantity precision for planning and production runs.\n *\n * Quantities are decimals held in binary floating point, so sums such as\n * `0.1 + 0.2` and products such as `2.2 * 100` carry rounding noise. Rather\n * than compare with a tolerance (which must grow with the value and so\n * eventually hides a whole unit), quantities are kept to\n * {@link QUANTITY_DECIMALS} decimal places: rounded through their decimal\n * representation, and summed and compared exactly as whole millionths.\n *\n * @packageDocumentation\n */\n\n/** Decimal places a quantity is kept to: a millionth of a unit. */\nexport const QUANTITY_DECIMALS = 6;\n\nconst SCALE = 10 ** QUANTITY_DECIMALS;\n\n/**\n * The largest quantity a production run accepts (target or report): every\n * value up to it with {@link QUANTITY_DECIMALS} places has at most 15\n * significant digits, so it survives a round trip through a double exactly\n * and sums of them stay exact.\n */\nexport const MAX_QUANTITY = 999_999_999;\n\n/**\n * `value` rounded to {@link QUANTITY_DECIMALS} decimal places (correctly\n * rounded through its decimal representation, so `0.1 + 0.2` becomes\n * exactly `0.3`).\n */\nexport function roundQuantity(value: number): number {\n return Number(value.toFixed(QUANTITY_DECIMALS));\n}\n\n/**\n * `value` as a whole number of millionths, exactly (read from its decimal\n * representation, not multiplied). Exact for any value up to\n * {@link MAX_QUANTITY}.\n */\nexport function toMillionths(value: number): number {\n return Number(value.toFixed(QUANTITY_DECIMALS).replace('.', ''));\n}\n\n/** A whole number of millionths back as a quantity. */\nexport function fromMillionths(millionths: number): number {\n return millionths / SCALE;\n}\n","/**\n * ProductionRun — a build of some quantity of a product against one\n * {@link BillOfMaterials}, reported as it happens: \"25 to build, 12 done\".\n *\n * The run pins the bill it was started against, carries the target and the\n * completed quantity, and moves through `planned` → `in_progress` → `done`,\n * or to `cancelled`. Each report of finished units is a dated\n * {@link ProductionRunCompletion}; `completedQty` is their sum.\n *\n * Writes go through `ProductionRunService`, which keeps `completedQty` equal\n * to the completions under concurrent reporting and enforces the status\n * transitions. The generated REST/MCP/CLI surfaces are read-only for that\n * reason.\n *\n * This is the shop-floor record of building. It is not the commercial\n * instruction to make goods (`ProductionOrder` in `@happyvertical/smrt-commerce`,\n * which carries terms and money); several runs may serve one order, or none\n * (make to stock). An application that has both links them itself.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { roundQuantity } from '../quantity.js';\nimport { BillOfMaterials } from './BillOfMaterials.js';\n\n/**\n * Where a {@link ProductionRun} is:\n *\n * - `planned` — created, nothing reported yet.\n * - `in_progress` — started, or at least one completion reported.\n * - `done` — the target was reached, or the run was finished short.\n * - `cancelled` — stopped; completions already reported stay.\n */\nexport type ProductionRunStatus =\n | 'planned'\n | 'in_progress'\n | 'done'\n | 'cancelled';\n\n/** Every {@link ProductionRunStatus}, in lifecycle order. */\nexport const PRODUCTION_RUN_STATUSES: readonly ProductionRunStatus[] = [\n 'planned',\n 'in_progress',\n 'done',\n 'cancelled',\n];\n\n/**\n * Options accepted by the {@link ProductionRun} constructor.\n */\nexport interface ProductionRunOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n bomId?: string;\n targetQty?: number;\n completedQty?: number;\n status?: ProductionRunStatus;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_production_runs',\n // Writes go through ProductionRunService, which serializes completions and\n // enforces the status transitions; generated create/update would bypass it.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class ProductionRun extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the run is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The bill the run builds against, pinned when the run is created.\n * Restrict: a bill with runs is history and is not deleted.\n */\n @foreignKey(BillOfMaterials, { required: true, onDelete: 'RESTRICT' })\n bomId: string = '';\n\n /** Units of the bill's product to build. Decimal; greater than zero. */\n @field({ type: 'decimal' })\n targetQty: number = 0.0;\n\n /** Units reported done so far: the sum of the run's completions. */\n @field({ type: 'decimal' })\n completedQty: number = 0.0;\n\n /** Lifecycle status; see {@link ProductionRunStatus}. */\n @field({ required: true })\n status: ProductionRunStatus = 'planned';\n\n constructor(options: ProductionRunOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.bomId !== undefined) this.bomId = options.bomId;\n if (options.targetQty !== undefined) this.targetQty = options.targetQty;\n if (options.completedQty !== undefined)\n this.completedQty = options.completedQty;\n if (options.status !== undefined) this.status = options.status;\n }\n\n /**\n * Units still to build, at the six-decimal quantity precision; `0` once\n * the target is reached.\n */\n remainingQty(): number {\n return Math.max(\n 0,\n roundQuantity(Number(this.targetQty) - Number(this.completedQty)),\n );\n }\n\n /** Refuses a status outside the lifecycle and impossible quantities. */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (!PRODUCTION_RUN_STATUSES.includes(this.status))\n throw new ValidationError(\n `Unknown production run status: ${String(this.status)}`,\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'status' },\n );\n const target = Number(this.targetQty);\n const completed = Number(this.completedQty);\n if (!Number.isFinite(target) || target <= 0)\n throw new ValidationError(\n 'A production run needs a target quantity greater than zero.',\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'targetQty' },\n );\n if (!Number.isFinite(completed) || completed < 0)\n throw new ValidationError(\n 'A production run cannot have a negative completed quantity.',\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'completedQty' },\n );\n }\n}\n","/**\n * ProductionRunCollection — query helpers for {@link ProductionRun} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n ProductionRun,\n type ProductionRunStatus,\n} from '../models/ProductionRun.js';\n\nexport class ProductionRunCollection extends SmrtCollection<ProductionRun> {\n static readonly _itemClass = ProductionRun;\n\n /** Runs against one bill, newest first. */\n async findByBom(bomId: string): Promise<ProductionRun[]> {\n return this.list({ where: { bomId }, orderBy: 'created_at DESC' });\n }\n\n /** Runs in one status, oldest first (a work queue). */\n async findByStatus(status: ProductionRunStatus): Promise<ProductionRun[]> {\n return this.list({ where: { status }, orderBy: 'created_at ASC' });\n }\n}\n","/**\n * ProductionRunCompletion — one dated report of finished units on a\n * {@link ProductionRun} (\"12 done on Tuesday\").\n *\n * Written only by `ProductionRunService.recordCompletion`, in the same\n * transaction that adds `qty` to the run's `completedQty` and, when the\n * caller asks, posts the stock movements; those movements carry\n * `sourceType: 'ProductionRunCompletion'` and this row's id. Read-only over\n * the generated surfaces.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { ProductionRun } from './ProductionRun.js';\n\n/**\n * Options accepted by the {@link ProductionRunCompletion} constructor.\n */\nexport interface ProductionRunCompletionOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n runId?: string;\n qty?: number;\n completedAt?: Date | string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_production_run_completions',\n // Written only by ProductionRunService.recordCompletion.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class ProductionRunCompletion extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The run the units were built on. Cascade: completions belong to their run. */\n @foreignKey(ProductionRun, { required: true, onDelete: 'CASCADE' })\n runId: string = '';\n\n /** Units finished in this report. Decimal; greater than zero. */\n @field({ type: 'decimal' })\n qty: number = 0.0;\n\n /** When the units were finished (defaults to when the report was made). */\n completedAt: Date = new Date();\n\n constructor(options: ProductionRunCompletionOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.runId !== undefined) this.runId = options.runId;\n if (options.qty !== undefined) this.qty = options.qty;\n if (options.completedAt !== undefined)\n this.completedAt =\n options.completedAt instanceof Date\n ? options.completedAt\n : new Date(options.completedAt);\n }\n\n /** Refuses a report of zero, negative or non-finite units. */\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n const qty = Number(this.qty);\n if (!Number.isFinite(qty) || qty <= 0)\n throw new ValidationError(\n 'A completion needs a quantity greater than zero.',\n 'MANUFACTURING_PRODUCTION_RUN_INVALID',\n { field: 'qty' },\n );\n }\n}\n","/**\n * ProductionRunCompletionCollection — query helpers for\n * {@link ProductionRunCompletion} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ProductionRunCompletion } from '../models/ProductionRunCompletion.js';\n\nexport class ProductionRunCompletionCollection extends SmrtCollection<ProductionRunCompletion> {\n static readonly _itemClass = ProductionRunCompletion;\n\n /** A run's completions in the order they happened. */\n async findByRun(runId: string): Promise<ProductionRunCompletion[]> {\n return this.list({\n where: { runId },\n orderBy: ['completedAt ASC', 'created_at ASC'],\n });\n }\n}\n","/**\n * RoutingStep — one operation in the routing of a {@link BillOfMaterials}.\n *\n * A routing is the ordered list of steps for building one unit of the bill's\n * product. It is optional: a bill with no routing steps behaves exactly as a\n * bill always has. Steps are ordered by `sequence`, unique within the bill.\n *\n * Cross-package-style references (`bomId`, `operationId`) are plain string ids,\n * as elsewhere in this package.\n *\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterials } from './BillOfMaterials.js';\nimport { Operation } from './Operation.js';\n\n/**\n * Options accepted by the {@link RoutingStep} constructor.\n */\nexport interface RoutingStepOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n bomId?: string;\n operationId?: string;\n sequence?: number;\n estimatedMinutes?: number;\n notes?: string;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'manufacturing_routing_steps',\n // One step per position per bill per tenant.\n conflictColumns: ['bom_id', 'sequence', 'tenant_id'],\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class RoutingStep extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-manufacturing\";\n /** Tenant scope. `null` means the step is a global record. */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The owning {@link BillOfMaterials}. Cascade: a routing belongs to its bill.\n * (`BomLine.bomId` is still a plain string; see AGENTS.md.)\n */\n @foreignKey(BillOfMaterials, { required: true, onDelete: 'CASCADE' })\n bomId: string = '';\n\n /** The {@link Operation} performed here. Restrict: operations are retired, never deleted. */\n @foreignKey(Operation, { required: true, onDelete: 'RESTRICT' })\n operationId: string = '';\n\n /** Position in the routing, starting at 1 and without gaps. */\n @field({ required: true })\n sequence: number = 1;\n\n /**\n * Estimated working minutes at this step to build one unit of the bill's\n * product. Decimal so `7.5` round-trips.\n */\n @field({ type: 'decimal' })\n estimatedMinutes: number = 0.0;\n\n /** Optional free-form note for this step. */\n notes: string = '';\n\n constructor(options: RoutingStepOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.bomId !== undefined) this.bomId = options.bomId;\n if (options.operationId !== undefined)\n this.operationId = options.operationId;\n if (options.sequence !== undefined) this.sequence = options.sequence;\n if (options.estimatedMinutes !== undefined)\n this.estimatedMinutes = options.estimatedMinutes;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n}\n","/**\n * RoutingStepCollection — query helpers for {@link RoutingStep} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { RoutingStep } from '../models/RoutingStep.js';\n\nexport class RoutingStepCollection extends SmrtCollection<RoutingStep> {\n static readonly _itemClass = RoutingStep;\n\n /** Return the routing of a bill in step order; empty when it has none. */\n async findByBom(bomId: string): Promise<RoutingStep[]> {\n return this.list({ where: { bomId }, orderBy: 'sequence ASC' });\n }\n\n /** Return every step that uses the given operation, across bills. */\n async findByOperation(operationId: string): Promise<RoutingStep[]> {\n return this.list({ where: { operationId }, orderBy: 'bomId ASC' });\n }\n}\n","/**\n * Types and error classes for operations, routing and the labour estimate.\n *\n * Strictly industry-neutral.\n *\n * @packageDocumentation\n */\n\nimport type { Operation } from './models/Operation.js';\nimport type { SubAssemblyLabour } from './types.js';\n\n/**\n * Callback that returns the hourly labour rate for an operation, in the bill's\n * currency, or `null` / `undefined` when no rate is known. The package holds no\n * rates: the caller decides where they come from (a price list, a rate class\n * the application keeps against the operation code, a timesheet rate). Mirrors\n * `ComponentCostResolver` for materials.\n */\nexport type OperationRateResolver = (\n operation: Operation,\n) => Promise<number | null | undefined> | number | null | undefined;\n\n/** One step of {@link LabourEstimate}. */\nexport interface RoutingStepEstimate {\n /** The routing step id. */\n stepId: string;\n /** Position in the routing, starting at 1. */\n sequence: number;\n /** Plain string id of the operation. */\n operationId: string;\n /** Operation code, or `''` if the operation row is missing. */\n operationCode: string;\n /** Operation name, or `''` if the operation row is missing. */\n operationName: string;\n /** Estimated working minutes per produced unit. */\n estimatedMinutes: number;\n /** Hourly rate from the resolver, or `0` when unavailable. */\n hourlyRate: number;\n /** `estimatedMinutes / 60 * hourlyRate`; `0` when the rate is unavailable. */\n stepCost: number;\n /** `true` when no resolver was supplied or it returned no rate. */\n rateUnavailable: boolean;\n}\n\n/**\n * Estimated labour per produced unit, returned by\n * `BomService.computeLabourEstimate`. A bill with no routing returns an empty\n * `steps` array and zero totals.\n */\nexport interface LabourEstimate {\n /** The bill that was rolled up. */\n bomId: string;\n /** `true` when the bill has at least one routing step. */\n hasRouting: boolean;\n /** Total estimated working minutes per produced unit. */\n totalMinutes: number;\n /** Total estimated labour cost per unit; counts only steps with a rate. */\n totalCost: number;\n /** ISO 4217 currency code of the bill. */\n currency: string;\n /** Steps in routing order. */\n steps: RoutingStepEstimate[];\n /** `true` when a step's rate was unavailable, so `totalCost` is a lower bound. */\n hasMissingRates: boolean;\n /**\n * Multi-level only (`{ levels }` above 1): every sub-assembly the walk\n * opened, at any depth, with its own routing estimate and how many units of\n * it one unit of this bill's product needs. `totalMinutes`, `totalCost`,\n * `hasRouting` and `hasMissingRates` then cover them too; `steps` stays this\n * bill's own routing.\n */\n subAssemblies?: SubAssemblyLabour[];\n}\n\n/** One step handed to `RoutingService.replaceRouting`. */\nexport interface RoutingStepInput {\n /** Plain string id of the operation to perform. */\n operationId: string;\n /** Estimated working minutes per produced unit; zero or more. */\n estimatedMinutes: number;\n /** Optional free-form note. */\n notes?: string;\n}\n\n/** Input for `OperationService.define`. */\nexport interface DefineOperationInput {\n /** Unique per tenant; not changed afterwards. */\n code: string;\n name: string;\n category?: string;\n /** Plain string id of a `smrt-human-resources` qualification definition. */\n requiredQualificationId?: string;\n}\n\n/** Thrown when an operation id does not exist. */\nexport class OperationNotFoundError extends Error {\n override name = 'OperationNotFoundError';\n\n constructor(public readonly operationId: string) {\n super(`Operation not found: ${operationId}`);\n }\n}\n\n/** Thrown when defining an operation whose code is already in use. */\nexport class DuplicateOperationCodeError extends Error {\n override name = 'DuplicateOperationCodeError';\n\n constructor(public readonly code: string) {\n super(`An operation with code \"${code}\" already exists.`);\n }\n}\n\n/** Thrown when a routing would newly add a retired operation. */\nexport class OperationRetiredError extends Error {\n override name = 'OperationRetiredError';\n\n constructor(public readonly operationId: string) {\n super(\n `Operation ${operationId} is retired and cannot be added to a routing.`,\n );\n }\n}\n\n/** Thrown for invalid operation or routing input. */\nexport class InvalidOperationInputError extends Error {\n override name = 'InvalidOperationInputError';\n}\n","/**\n * Inputs, results and errors for `ProductionRunService`.\n *\n * @packageDocumentation\n */\n\nimport type { ProductionRun } from './models/ProductionRun.js';\nimport type { ProductionRunCompletion } from './models/ProductionRunCompletion.js';\nimport type {\n ConsumeResult,\n ProduceResult,\n} from './services/ProductionService.js';\n\n/** Input for `ProductionRunService.createRun`. Give `bomId`, or `productId` for its active bill. */\nexport interface CreateProductionRunInput {\n /** The bill to build against. Wins over `productId`. */\n bomId?: string;\n /** A product (usually an `Assembly`) whose active bill the run pins. */\n productId?: string;\n /** Units to build; greater than zero. */\n targetQty: number;\n}\n\n/** Stock to consume when a completion is recorded: the run's bill, for the completed units. */\nexport interface CompletionConsumeOptions {\n /** Location the components are taken from. */\n locationId: string;\n /** Optional free-form note on each movement. */\n note?: string;\n}\n\n/** Finished goods to receive when a completion is recorded. */\nexport interface CompletionProduceOptions {\n /** Location the finished units are received into. */\n locationId: string;\n /** The SKU of the bill's product that was built. */\n finishedSkuId: string;\n /** Optional free-form note on the movement. */\n note?: string;\n}\n\n/** Input for `ProductionRunService.recordCompletion`. */\nexport interface RecordCompletionInput {\n /** Units finished; greater than zero and no more than the run has left. */\n qty: number;\n /** When they were finished. Defaults to now. */\n completedAt?: Date | string;\n /**\n * Consume the run's bill for `qty` units, in the same transaction. Omit\n * (the default) to record progress without touching stock.\n */\n consume?: CompletionConsumeOptions;\n /**\n * Receive `qty` finished units, in the same transaction. Omit (the\n * default) to record progress without touching stock.\n */\n produce?: CompletionProduceOptions;\n}\n\n/** Result of `ProductionRunService.recordCompletion`. */\nexport interface RecordCompletionResult {\n /** The run after the completion. */\n run: ProductionRun;\n /** The completion that was recorded. */\n completion: ProductionRunCompletion;\n /** Components consumed; empty unless `consume` was given. */\n consumed: ConsumeResult[];\n /** Finished goods received; `null` unless `produce` was given. */\n produced: ProduceResult | null;\n}\n\n/** Thrown when a production run id does not exist. */\nexport class ProductionRunNotFoundError extends Error {\n override name = 'ProductionRunNotFoundError';\n\n constructor(public readonly runId: string) {\n super(`Production run not found: ${runId}`);\n }\n}\n\n/** Thrown when an action is not allowed in the run's current status. */\nexport class ProductionRunStateError extends Error {\n override name = 'ProductionRunStateError';\n\n constructor(\n public readonly runId: string,\n /** The run's status when the action was refused. */\n public readonly status: string,\n /** What was attempted: `start`, `complete`, `finish`, `cancel`, `retarget`. */\n public readonly action: string,\n ) {\n super(`Production run ${runId} is ${status}; cannot ${action} it.`);\n }\n}\n\n/** Thrown when a completion would take the run past its target. */\nexport class ProductionRunOverCompletionError extends Error {\n override name = 'ProductionRunOverCompletionError';\n\n constructor(\n public readonly runId: string,\n /** The quantity reported. */\n public readonly qty: number,\n /** What the run had left to build. */\n public readonly remaining: number,\n ) {\n super(\n `Production run ${runId} has ${remaining} left to build; cannot report ${qty}.`,\n );\n }\n}\n\n/** Thrown for invalid production run input (quantities, dates, bill). */\nexport class InvalidProductionRunInputError extends Error {\n override name = 'InvalidProductionRunInputError';\n}\n","/**\n * Reading a structure in its owner's tenant: the owner's rows and global\n * ones, whatever tenant context is active.\n *\n * Under a tenant context the tenancy interceptor narrows every read to that\n * tenant, which hides global rows (a shared material, a global bill, global\n * stock); without a context it narrows nothing, which shows every tenant's\n * rows. Neither is a bill's structure. {@link readOwnAndGlobal} runs list\n * reads through `withTenantGlobalRead` (tenant rows plus global rows, refused\n * for a tenant the active context may not read), and callers keep\n * {@link isOwnOrGlobal} rows, which also covers tenancy being disabled.\n *\n * @packageDocumentation\n */\n\nimport {\n getCurrentTenant,\n withTenantGlobalRead,\n} from '@happyvertical/smrt-tenancy';\n\n/**\n * Run `read` so its list reads return `owner`'s rows and global ones. For a\n * global owner (`null`), reads see the active tenant's rows and global ones,\n * and callers keep the global rows.\n */\nexport function readOwnAndGlobal<T>(\n owner: string | null,\n read: () => Promise<T>,\n): Promise<T> {\n const scope = owner ?? getCurrentTenant()?.tenantId ?? null;\n return scope ? withTenantGlobalRead(scope, read) : read();\n}\n\n/** `true` for a global row or one of `owner`'s rows. */\nexport function isOwnOrGlobal(\n row: { tenantId?: string | null },\n owner: string | null,\n): boolean {\n const rowTenant = row.tenantId ?? null;\n return rowTenant === null || rowTenant === owner;\n}\n","/**\n * AssemblyService — the link between a bill line and the thing it names.\n *\n * A {@link BomLine} points at a component SKU. That SKU belongs to a\n * `Product` row in `@happyvertical/smrt-products`, and the row's subtype\n * decides what the line means:\n *\n * | Component SKU belongs to | Kind | Meaning |\n * |---|---|---|\n * | an {@link Assembly} | `assembly` | a sub-assembly; it is built from its own active bill, if it has one |\n * | a `Material` | `material` | a consumed input; the leaf of the structure |\n * | any other `Product` | `bought` | an item that is bought, not built here |\n * | nothing (unknown SKU or product) | `missing` | the line points at a record that does not exist |\n *\n * {@link AssemblyService.resolveComponent} answers that question for one\n * SKU, and also returns the assembly's active bill. It is the step a\n * multi-level explosion repeats: resolve each line's component, recurse into\n * every `assembly` that has an `activeBom`, stop at everything else.\n *\n * The service also owns the **cycle refusal**: an assembly may not contain\n * itself, directly or through sub-assemblies. {@link BomLine} and\n * {@link BillOfMaterials} call {@link AssemblyService.assertLineAcyclic} and\n * {@link AssemblyService.assertBillAcyclic} before every save, so the\n * generated REST routes refuse a cycle too.\n *\n * @packageDocumentation\n */\n\nimport {\n type DatabaseConfig,\n resolveDatabase,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport {\n ProductCollection,\n SkuCollection,\n} from '@happyvertical/smrt-products/collections';\nimport {\n Material,\n type Product,\n type Sku,\n} from '@happyvertical/smrt-products/models';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { BomLineCollection } from '../collections/BomLineCollection.js';\nimport { Assembly } from '../models/Assembly.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport { readOwnAndGlobal } from '../tenant-scope.js';\nimport { BomNotFoundError } from '../types.js';\n\n/**\n * What a component SKU is, as far as building goes. See the table in the\n * {@link AssemblyService} module documentation.\n */\nexport type ComponentKind = 'assembly' | 'material' | 'bought' | 'missing';\n\n/**\n * The answer to \"what is this component SKU?\" returned by\n * {@link AssemblyService.resolveComponent}.\n */\nexport interface ResolvedComponent {\n /** The component SKU id that was resolved. */\n skuId: string;\n /** What the SKU is; see {@link ComponentKind}. */\n kind: ComponentKind;\n /** The SKU row, or `null` when no SKU has this id. */\n sku: Sku | null;\n /**\n * The product the SKU belongs to (an `Assembly`, a `Material` or a plain\n * `Product`), or `null` when the SKU or its product does not exist.\n */\n product: Product | null;\n /** The assembly, when {@link kind} is `assembly`; otherwise `null`. */\n assembly: Assembly | null;\n /**\n * The assembly's active bill (the highest active version), or `null` for\n * an assembly that has no active bill yet and for every other kind.\n */\n activeBom: BillOfMaterials | null;\n /**\n * `true` only for an assembly with an active bill: the component *can* be\n * built here. Whether a shortage is built or bought is the application's\n * decision; this only reports the fact.\n */\n buildable: boolean;\n}\n\n/** One bill line together with the component it resolves to. */\nexport interface BillStructureLine {\n /** The bill line as stored. */\n line: BomLine;\n /** The line's component SKU, resolved. */\n component: ResolvedComponent;\n}\n\n/**\n * One level of an assembly's structure, returned by\n * {@link AssemblyService.getBillStructure}. Sub-assembly lines carry their\n * own `activeBom`; pass its id back to `getBillStructure` to open the next\n * level.\n */\nexport interface BillStructure {\n /** The bill that was read. */\n bom: BillOfMaterials;\n /** Its lines in component-SKU order, each resolved. */\n lines: BillStructureLine[];\n}\n\n/** One product on a cycle path reported by {@link BomCycleError}. */\nexport interface BomCyclePathEntry {\n /** The product id (an assembly, or the bill's own product). */\n productId: string;\n /** The product's name, or its id when it has no name or cannot be read. */\n name: string;\n}\n\n/**\n * Thrown when saving a bill line, or saving a bill as active, would make a\n * product contain itself. `path` starts and ends with the bill's product,\n * listing each assembly in between: `Frame → Side panel → Frame`.\n */\nexport class BomCycleError extends ValidationError {\n override name = 'BomCycleError';\n\n constructor(\n /** The bill being saved (or the bill of the line being saved). */\n public readonly bomId: string,\n /** The cycle, from the bill's product back to itself. */\n public readonly path: BomCyclePathEntry[],\n ) {\n const first = path[0]?.name ?? '';\n super(\n `Refused: \"${first}\" would contain itself: ${path\n .map((entry) => entry.name)\n .join(' → ')}`,\n 'MANUFACTURING_BOM_CYCLE',\n { bomId, path },\n );\n }\n}\n\n/** Options for {@link AssemblyService.resolveComponent} and the walks built on it. */\nexport interface ResolveComponentOptions {\n /**\n * The tenant whose structure is being read. When given (including `null`\n * for global records), an assembly's active bill is chosen only among\n * bills with exactly this `tenantId`, and a SKU, product or bill line\n * belonging to another tenant is treated as missing (global rows stay\n * visible), whatever tenant context is active. When omitted, the active\n * tenant context decides what is visible.\n */\n tenantId?: string | null;\n}\n\n/** Options accepted by {@link AssemblyService.create}. */\nexport interface AssemblyServiceOptions {\n /** Database config or live handle shared by every collection. */\n db: DatabaseConfig;\n}\n\n// Ids are UUIDs on every adapter. A value that is not one cannot name a row,\n// and passing it to a UUID column on PostgreSQL is a cast error, so it is\n// resolved as `missing` without a query.\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Whether a row belongs to the structure being read: with an explicit\n * `tenantId`, a global row (`tenantId` null) or one of that tenant's rows;\n * without one, whatever the active tenant context returned.\n */\nfunction visibleTo(\n row: { tenantId?: string | null },\n options: ResolveComponentOptions,\n): boolean {\n if (options.tenantId === undefined) return true;\n const rowTenant = row.tenantId ?? null;\n return rowTenant === null || rowTenant === options.tenantId;\n}\n\nfunction isUuid(value: string | null | undefined): value is string {\n return typeof value === 'string' && UUID_PATTERN.test(value);\n}\n\n/**\n * Resolves components and refuses cycles. Construct with\n * {@link AssemblyService.create} or {@link createAssemblyService}.\n *\n * @example\n * ```typescript\n * const assemblies = await AssemblyService.create({ db });\n * const component = await assemblies.resolveComponent(line.componentSkuId);\n * if (component.kind === 'assembly' && component.activeBom) {\n * // a sub-assembly that can be built: walk component.activeBom next\n * }\n * ```\n */\nexport class AssemblyService {\n private constructor(\n /** Every `Product` row, any subtype. */\n public readonly products: ProductCollection,\n /** The SKU catalog. */\n public readonly skus: SkuCollection,\n /** Bills of materials. */\n public readonly boms: BillOfMaterialsCollection,\n /** Bill lines. */\n public readonly lines: BomLineCollection,\n ) {}\n\n /** Factory — prefer {@link createAssemblyService}. */\n static async create(\n options: AssemblyServiceOptions,\n ): Promise<AssemblyService> {\n // Resolve once so every collection shares one connection: separate\n // `create` calls with a config object such as `:memory:` would each open\n // their own database.\n const db = (await resolveDatabase(options.db)) as unknown as DatabaseConfig;\n const [products, skus, boms, lines] = await Promise.all([\n ProductCollection.create({ db }),\n SkuCollection.create({ db }),\n BillOfMaterialsCollection.create({ db }),\n BomLineCollection.create({ db }),\n ]);\n return new AssemblyService(products, skus, boms, lines);\n }\n\n /**\n * Resolve a component SKU to what it is: an assembly (with its active bill,\n * or `null` when it has none), a material, a bought item, or missing. Never\n * throws for an unknown id; it reports `missing` instead.\n *\n * This is the documented link a multi-level explosion walks: recurse into\n * `activeBom` while `kind === 'assembly'`, stop at every other kind. Pass\n * the parent bill's `tenantId` so a shared assembly resolves to that\n * tenant's bill, not another tenant's.\n */\n async resolveComponent(\n skuId: string,\n options: ResolveComponentOptions = {},\n ): Promise<ResolvedComponent> {\n const missing = (sku: Sku | null): ResolvedComponent => ({\n skuId,\n kind: 'missing',\n sku,\n product: null,\n assembly: null,\n activeBom: null,\n buildable: false,\n });\n\n if (!isUuid(skuId)) return missing(null);\n // With an explicit tenant, the SKU and product are read as that tenant's\n // or global (a shared material stays visible under a tenant context); one\n // of another tenant is reported as missing, never resolved.\n const sku = await this.byId(this.skus, skuId, options);\n if (!sku || !visibleTo(sku, options)) return missing(null);\n if (!isUuid(sku.productId)) return missing(sku);\n const product = await this.byId(this.products, sku.productId, options);\n if (!product || !visibleTo(product, options)) return missing(sku);\n\n if (product instanceof Assembly) {\n const activeBom = await this.findActiveBom(product.id!, options);\n return {\n skuId,\n kind: 'assembly',\n sku,\n product,\n assembly: product,\n activeBom,\n buildable: activeBom !== null,\n };\n }\n\n return {\n skuId,\n kind: product instanceof Material ? 'material' : 'bought',\n sku,\n product,\n assembly: null,\n activeBom: null,\n buildable: false,\n };\n }\n\n /**\n * The highest-version active bill for a product. With `options.tenantId`,\n * that tenant's own bill, or else a global one; another tenant's never.\n */\n async findActiveBom(\n productId: string,\n options: ResolveComponentOptions = {},\n ): Promise<BillOfMaterials | null> {\n const owner = options.tenantId;\n if (owner === undefined) return this.boms.findActiveForProduct(productId);\n const active = await readOwnAndGlobal(owner, () =>\n this.boms.list({\n where: { productId, status: 'active' },\n orderBy: 'version DESC',\n }),\n );\n return (\n active.find((bom) => (bom.tenantId ?? null) === owner) ??\n active.find((bom) => (bom.tenantId ?? null) === null) ??\n null\n );\n }\n\n /**\n * One row by id: through the collection's `get` without an explicit\n * tenant, else as a list read of that tenant's and global rows.\n */\n private async byId<T extends { tenantId?: string | null }>(\n collection: {\n get(filter: { id: string }): Promise<T | null>;\n list(options: { where: { id: string }; limit: number }): Promise<T[]>;\n },\n id: string,\n options: ResolveComponentOptions,\n ): Promise<T | null> {\n const owner = options.tenantId;\n if (owner === undefined) return collection.get({ id });\n const rows = await readOwnAndGlobal(owner, () =>\n collection.list({ where: { id }, limit: 1 }),\n );\n return rows[0] ?? null;\n }\n\n /** `true` when the SKU belongs to an {@link Assembly}. */\n async isAssembly(skuId: string): Promise<boolean> {\n return (await this.resolveComponent(skuId)).kind === 'assembly';\n }\n\n /**\n * Read one bill and resolve every line's component. Sub-assembly lines say\n * whether they have an active bill of their own (`component.activeBom`);\n * the caller opens the next level by passing that id back in.\n *\n * @throws {BomNotFoundError} when no bill has this id.\n */\n async getBillStructure(bomId: string): Promise<BillStructure> {\n const bom = isUuid(bomId) ? await this.boms.get({ id: bomId }) : null;\n if (!bom) throw new BomNotFoundError(bomId);\n const cache = new Map<string, Promise<ResolvedComponent>>();\n const scope = { tenantId: bom.tenantId ?? null };\n const lines = (\n await readOwnAndGlobal(scope.tenantId, () => this.lines.findByBom(bomId))\n ).filter((line) => visibleTo(line, scope));\n const resolved: BillStructureLine[] = [];\n for (const line of lines) {\n resolved.push({\n line,\n component: await this.resolveCached(line.componentSkuId, cache, scope),\n });\n }\n return { bom, lines: resolved };\n }\n\n /**\n * Find the path by which adding `componentSkuIds` to a bill for\n * `productId` would make that product contain itself, or `null` when none\n * would. The walk follows each sub-assembly's **active** bill only; a\n * draft or superseded bill is not part of the structure. Pass the bill's\n * `tenantId` so the walk reads that tenant's structure.\n */\n async findCycle(\n productId: string,\n componentSkuIds: readonly string[],\n options: ResolveComponentOptions = {},\n ): Promise<BomCyclePathEntry[] | null> {\n const cache = new Map<string, Promise<ResolvedComponent>>();\n const visited = new Set<string>();\n\n const walk = async (\n skuIds: readonly string[],\n trail: Product[],\n ): Promise<Product[] | null> => {\n for (const skuId of skuIds) {\n const component = await this.resolveCached(skuId, cache, options);\n const product = component.product;\n if (!product?.id) continue;\n if (product.id === productId) return [...trail, product];\n if (!component.activeBom || visited.has(product.id)) continue;\n visited.add(product.id);\n const billId = component.activeBom.id!;\n const lines = (\n options.tenantId === undefined\n ? await this.lines.findByBom(billId)\n : await readOwnAndGlobal(options.tenantId, () =>\n this.lines.findByBom(billId),\n )\n ).filter((line) => visibleTo(line, options));\n const found = await walk(\n lines.map((line) => line.componentSkuId),\n [...trail, product],\n );\n if (found) return found;\n }\n return null;\n };\n\n const trail = await walk(componentSkuIds, []);\n if (!trail) return null;\n const self = trail[trail.length - 1];\n return [self, ...trail].map((product) => ({\n productId: product.id!,\n name: product.name || product.id!,\n }));\n }\n\n /**\n * Refuse a bill line that would make its bill's product contain itself.\n * A line on a draft bill is checked too, so a cycle is refused when it is\n * written rather than when the bill is activated. A line whose bill does\n * not exist is left to the caller.\n *\n * @throws {BomCycleError} naming the path.\n */\n async assertLineAcyclic(\n line: Pick<BomLine, 'bomId' | 'componentSkuId'>,\n ): Promise<void> {\n if (!isUuid(line.bomId)) return;\n const bom = await this.boms.get({ id: line.bomId });\n if (!bom) return;\n const path = await this.findCycle(bom.productId, [line.componentSkuId], {\n tenantId: bom.tenantId ?? null,\n });\n if (path) throw new BomCycleError(line.bomId, path);\n }\n\n /**\n * Refuse saving a bill as `active` when any of its lines would make its\n * product contain itself. Bills in any other status are not part of the\n * structure and pass.\n *\n * The lines checked are the bill's own and those of any stored bill with\n * the same natural key (`productId`, `version`, and the bill's tenant, or\n * the active tenant when the bill has none yet): saving a new\n * instance with that key upserts onto the stored bill and activates its\n * lines, so they are part of what the save makes active.\n *\n * @throws {BomCycleError} naming the path.\n */\n async assertBillAcyclic(\n bom: Pick<\n BillOfMaterials,\n 'id' | 'productId' | 'status' | 'version' | 'tenantId'\n >,\n ): Promise<void> {\n if (bom.status !== 'active') return;\n const billIds = new Set<string>();\n if (isUuid(bom.id)) billIds.add(bom.id);\n // Validation runs before the tenancy interceptor fills `tenantId`, so an\n // omitted tenant is the one the save will be stamped with.\n const tenantId = bom.tenantId ?? getCurrentTenant()?.tenantId ?? null;\n const sameKey = await readOwnAndGlobal(tenantId, () =>\n this.boms.list({\n where: { productId: bom.productId, version: bom.version },\n }),\n );\n for (const stored of sameKey) {\n if (stored.id && (stored.tenantId ?? null) === tenantId)\n billIds.add(stored.id);\n }\n const componentSkuIds: string[] = [];\n for (const billId of billIds) {\n for (const line of await readOwnAndGlobal(tenantId, () =>\n this.lines.findByBom(billId),\n ))\n if (visibleTo(line, { tenantId }))\n componentSkuIds.push(line.componentSkuId);\n }\n if (componentSkuIds.length === 0) return;\n const path = await this.findCycle(bom.productId, componentSkuIds, {\n tenantId,\n });\n if (path) throw new BomCycleError(bom.id || [...billIds][0] || '', path);\n }\n\n private resolveCached(\n skuId: string,\n cache: Map<string, Promise<ResolvedComponent>>,\n options: ResolveComponentOptions,\n ): Promise<ResolvedComponent> {\n let pending = cache.get(skuId);\n if (!pending) {\n pending = this.resolveComponent(skuId, options);\n cache.set(skuId, pending);\n }\n return pending;\n }\n}\n\n/** Convenience factory for {@link AssemblyService}. */\nexport function createAssemblyService(\n options: AssemblyServiceOptions,\n): Promise<AssemblyService> {\n return AssemblyService.create(options);\n}\n","/**\n * The shared step of every multi-level walk in {@link BomService}: read a\n * bill's lines, resolve each component, and decide whether to open a\n * sub-assembly's active bill. Explosion, planning, cost and labour all walk\n * the same way through this class, so they agree on what is opened.\n *\n * A walk is bounded three ways: by the caller's `levels`, by\n * {@link MAX_EXPLOSION_DEPTH} and {@link MAX_EXPLOSION_LINES}, and by the path\n * of products above the current line, which turns a cycle already present in\n * stored data into a {@link BomStructureCycleError} instead of a loop.\n *\n * @internal\n * @packageDocumentation\n */\n\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport {\n BomExplosionLimitError,\n type ExplosionLevels,\n type ExplosionPathEntry,\n MAX_EXPLOSION_DEPTH,\n MAX_EXPLOSION_LINES,\n} from '../types.js';\nimport {\n type AssemblyService,\n BomCycleError,\n type BomCyclePathEntry,\n type ResolvedComponent,\n} from './AssemblyService.js';\n\n/**\n * Thrown when a multi-level walk meets a product that already contains\n * itself in stored data (a loop saved before cycle refusal existed, or\n * written around it). Carries the loop as `path`, like the\n * {@link BomCycleError} it extends.\n */\nexport class BomStructureCycleError extends BomCycleError {\n override name = 'BomStructureCycleError';\n\n constructor(bomId: string, path: BomCyclePathEntry[]) {\n super(bomId, path);\n this.message = `The bill structure contains a cycle: ${path\n .map((entry) => entry.name)\n .join(' → ')}`;\n }\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * The deepest level a walk may open for `levels`. Throws for anything that is\n * not a whole number from 1 to {@link MAX_EXPLOSION_DEPTH} or `'all'`.\n */\nexport function maxLevelFor(levels: ExplosionLevels | undefined): number {\n if (levels === undefined) return 1;\n if (levels === 'all') return MAX_EXPLOSION_DEPTH;\n if (!Number.isInteger(levels) || levels < 1 || levels > MAX_EXPLOSION_DEPTH)\n throw new Error(\n `levels must be a whole number from 1 to ${MAX_EXPLOSION_DEPTH}, or 'all' (got ${String(levels)})`,\n );\n return levels;\n}\n\n/** `true` when the caller asked for a walk rather than the single-level form. */\nexport function isMultiLevel(levels: ExplosionLevels | undefined): boolean {\n return maxLevelFor(levels) > 1;\n}\n\n/** One walk from one top bill; caches what it reads for the walk's lifetime. */\nexport class StructureWalk {\n private readonly components = new Map<string, Promise<ResolvedComponent>>();\n private readonly bills = new Map<string, Promise<BomLine[]>>();\n private lineCount = 0;\n /** The deepest level whose lines the walk reads. */\n readonly maxLevel: number;\n\n constructor(\n private readonly assemblies: AssemblyService,\n /** The bill the walk starts from. */\n readonly top: BillOfMaterials,\n /** The caller's `levels`. */\n readonly levels: ExplosionLevels,\n ) {\n this.maxLevel = maxLevelFor(levels);\n }\n\n /**\n * Resolve a component SKU, reading assemblies' active bills in the top\n * bill's tenant so a shared assembly opens that tenant's bill.\n */\n resolve(skuId: string): Promise<ResolvedComponent> {\n let pending = this.components.get(skuId);\n if (!pending) {\n pending = this.assemblies.resolveComponent(skuId, {\n tenantId: this.top.tenantId ?? null,\n });\n this.components.set(skuId, pending);\n }\n return pending;\n }\n\n /**\n * A bill's lines in the top bill's tenant (and global lines), counted\n * against {@link MAX_EXPLOSION_LINES} every time the walk visits them (a\n * shared sub-assembly counts once per use).\n */\n async linesOf(bomId: string): Promise<BomLine[]> {\n let pending = this.bills.get(bomId);\n if (!pending) {\n // The top bill's structure only: its tenant's lines and global ones,\n // as `AssemblyService` reads a structure.\n const tenantId = this.top.tenantId ?? null;\n pending = readOwnAndGlobal(tenantId, () =>\n this.assemblies.lines.findByBom(bomId),\n ).then((lines) => lines.filter((line) => isOwnOrGlobal(line, tenantId)));\n this.bills.set(bomId, pending);\n }\n const lines = await pending;\n this.lineCount += lines.length;\n if (this.lineCount > MAX_EXPLOSION_LINES)\n throw new BomExplosionLimitError(this.top.id as string, 'lines');\n return lines;\n }\n\n /** The path entry for the top bill. */\n async root(): Promise<ExplosionPathEntry> {\n const productId = this.top.productId;\n let name = productId;\n if (UUID_PATTERN.test(productId)) {\n // Only a name the top bill's tenant may see.\n const owner = this.top.tenantId ?? null;\n const [product] = await readOwnAndGlobal(owner, () =>\n this.assemblies.products.list({ where: { id: productId }, limit: 1 }),\n );\n if (product?.name && isOwnOrGlobal(product, owner)) name = product.name;\n }\n return { bomId: this.top.id as string, productId, name };\n }\n\n /**\n * The bill to open below a line at `level`, or `null` when the component\n * is not a sub-assembly with an active bill or `levels` stops here.\n *\n * @throws {BomStructureCycleError} when the sub-assembly is already on\n * `path` (it would contain itself).\n * @throws {BomExplosionLimitError} when `levels: 'all'` would go deeper\n * than {@link MAX_EXPLOSION_DEPTH}.\n */\n open(\n component: ResolvedComponent,\n level: number,\n path: readonly ExplosionPathEntry[],\n ): ExplosionPathEntry | null {\n const bom = component.activeBom;\n const product = component.product;\n if (!component.buildable || !bom?.id || !product?.id) return null;\n const name = product.name || product.id;\n const at = path.findIndex((entry) => entry.productId === product.id);\n if (at >= 0)\n throw new BomStructureCycleError(this.top.id as string, [\n ...path\n .slice(at)\n .map((entry) => ({ productId: entry.productId, name: entry.name })),\n { productId: product.id, name },\n ]);\n if (level >= this.maxLevel) {\n if (this.levels === 'all')\n throw new BomExplosionLimitError(this.top.id as string, 'depth');\n return null;\n }\n return { bomId: bom.id, productId: product.id, name };\n }\n}\n","/**\n * BomService — cost rollup, requirements explosion, and \"can we make this?\"\n * stock-availability checks for a {@link BillOfMaterials}.\n *\n * The service is the sanctioned planning surface for BOM-driven workflows.\n * It never mutates stock — it answers questions:\n *\n * - \"How much does it cost to make one unit?\" via {@link computeMaterialCost}\n * - \"What do I need to buy / pull to make N units?\" via {@link explodeRequirements}\n * - \"Do I have enough on hand?\" via {@link canProduce}\n *\n * Every method reads one level of the bill by default. Pass `{ levels }` to\n * walk sub-assemblies too: a line whose component SKU belongs to an\n * `Assembly` with an active bill is opened, to the given depth or (`'all'`)\n * down to materials and bought items. {@link explode} returns that walk line\n * by line with the rolled-up totals, and {@link planRequirements} nets it\n * against stock: how much of each sub-assembly is available, how much is\n * short, and what building the shortfall takes.\n *\n * Construct via {@link BomService.create} — the static factory wires up\n * the BOM, BOM-line, and inventory collections so a single `db` (or a\n * pre-built {@link StockService}) drives everything.\n *\n * Material cost resolution is pluggable. Pass a `costResolver` callback to\n * supply unit costs from anywhere — `@happyvertical/smrt-products`\n * (`Material.costPerUnit`), a purchase-order rolling average, a vendor\n * price book, anything. When no resolver is supplied, costs default to\n * `0` and the per-line `costUnavailable` flag is set so UIs can surface\n * \"missing cost data\" rather than silently rolling up a $0 BOM.\n *\n * @packageDocumentation\n */\n\nimport { type DatabaseConfig, resolveDatabase } from '@happyvertical/smrt-core';\nimport {\n createStockService,\n type StockService,\n} from '@happyvertical/smrt-inventory';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { BomLineCollection } from '../collections/BomLineCollection.js';\nimport { OperationCollection } from '../collections/OperationCollection.js';\nimport { RoutingStepCollection } from '../collections/RoutingStepCollection.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport type {\n LabourEstimate,\n OperationRateResolver,\n RoutingStepEstimate,\n} from '../operation-types.js';\nimport { roundQuantity } from '../quantity.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport {\n type BomCostRollup,\n type BomLineCost,\n BomNotFoundError,\n type CanProduceResult,\n type ExplodedLine,\n type Explosion,\n type ExplosionOptions,\n type ExplosionPathEntry,\n type MaterialRequirement,\n type MaterialShortage,\n type PlannedLine,\n type RequirementsPlan,\n type SubAssemblyLabour,\n} from '../types.js';\nimport { AssemblyService, type ResolvedComponent } from './AssemblyService.js';\nimport { isMultiLevel, StructureWalk } from './structure-walk.js';\n\n/**\n * Callback signature for resolving the unit cost of a component SKU.\n *\n * Implementations should return the latest known cost in the BOM's\n * currency, or `null` (or `undefined`) when no cost is available. The\n * service treats unresolved costs as `0` and marks the affected line so\n * callers can warn the user.\n *\n * @example\n * ```typescript\n * import { MaterialCollection } from '@happyvertical/smrt-products/models';\n *\n * const materials = await MaterialCollection.create({ db });\n *\n * const bom = await BomService.create({\n * db,\n * costResolver: async (componentSkuId) => {\n * const sku = await skus.get(componentSkuId);\n * if (!sku?.productId) return null;\n * const material = await materials.get(sku.productId);\n * return material?.costPerUnit ?? null;\n * },\n * });\n * ```\n */\nexport type ComponentCostResolver = (\n componentSkuId: string,\n) => Promise<number | null | undefined> | number | null | undefined;\n\n/**\n * Options accepted by {@link BomService.create}.\n *\n * Either provide a `db` for the service to construct its own internal\n * collections + {@link StockService}, or pass a pre-built `stockService`\n * to share one across subsystems.\n */\nexport type BomServiceOptions = {\n /**\n * Optional resolver that returns the latest known unit cost for a\n * component SKU. See {@link ComponentCostResolver}. When omitted, all\n * costs default to `0` and per-line `costUnavailable` is set.\n */\n costResolver?: ComponentCostResolver;\n /**\n * Optional resolver that returns the hourly labour rate for an operation,\n * used by {@link BomService.computeLabourEstimate}. When omitted, the\n * estimate reports minutes only and flags every step `rateUnavailable`.\n */\n rateResolver?: OperationRateResolver;\n} & (\n | { db: DatabaseConfig; stockService?: StockService }\n | { stockService: StockService; db?: DatabaseConfig }\n);\n\n/**\n * BOM-driven planning service. See module documentation for the role\n * this plays in the manufacturing pipeline.\n *\n * @example\n * ```typescript\n * const bomService = await BomService.create({ db });\n * const rollup = await bomService.computeMaterialCost(bom.id!);\n * const requirements = await bomService.explodeRequirements(bom.id!, 100);\n * const check = await bomService.canProduce(bom.id!, 100);\n * ```\n */\nexport class BomService {\n private constructor(\n public readonly boms: BillOfMaterialsCollection,\n public readonly lines: BomLineCollection,\n public readonly stockService: StockService,\n private readonly costResolver: ComponentCostResolver | undefined,\n public readonly routingSteps: RoutingStepCollection,\n public readonly operations: OperationCollection,\n private readonly rateResolver: OperationRateResolver | undefined,\n /** Resolves components for the multi-level walks. */\n public readonly assemblies: AssemblyService,\n ) {}\n\n /** Factory — prefer {@link createBomService}. */\n static async create(options: BomServiceOptions): Promise<BomService> {\n const stockService =\n options.stockService ?? (await buildStockService(options));\n // Share the same db that the StockService uses so reads always hit\n // one connection / pool. StockService exposes its `db` as a public\n // field specifically so downstream services can compose against it\n // without reaching into Collection internals.\n // Resolve once: a config object such as `{ type: 'sqlite', url: ':memory:' }`\n // would otherwise yield a separate database per collection.\n const sharedDb = (await resolveDatabase(\n options.stockService ? (options.db ?? stockService.db) : stockService.db,\n )) as unknown as DatabaseConfig;\n const [boms, lines, routingSteps, operations, assemblies] =\n await Promise.all([\n BillOfMaterialsCollection.create({ db: sharedDb }),\n BomLineCollection.create({ db: sharedDb }),\n RoutingStepCollection.create({ db: sharedDb }),\n OperationCollection.create({ db: sharedDb }),\n AssemblyService.create({ db: sharedDb }),\n ]);\n return new BomService(\n boms,\n lines,\n stockService,\n options.costResolver,\n routingSteps,\n operations,\n options.rateResolver,\n assemblies,\n );\n }\n\n /**\n * Walk every {@link BomLine} for the given BOM, resolve each line's\n * component cost, apply waste, and return the rolled-up material cost\n * per produced unit along with a per-line breakdown.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist.\n *\n * Lines whose cost cannot be resolved contribute `0` to the total and\n * set `costUnavailable: true` on their breakdown row; the aggregate\n * `hasMissingCosts` flag mirrors this so callers can surface a UI\n * warning.\n *\n * With `{ levels }` above 1, a sub-assembly line whose bill the walk opens\n * is costed from that bill instead of the resolver: its `unitCost` is the\n * bill's rolled-up material cost per unit, its `components` hold that\n * bill's lines, and a missing cost anywhere below marks it and\n * `hasMissingCosts` (its known part still counts, so the total is a lower\n * bound). Other lines, including sub-assemblies with no active bill, use\n * the resolver as before.\n */\n async computeMaterialCost(\n bomId: string,\n options: ExplosionOptions = {},\n ): Promise<BomCostRollup> {\n if (isMultiLevel(options.levels))\n return this.computeMultiLevelCost(bomId, options);\n const bom = await this.requireBom(bomId);\n const lines = await this.linesOf(bom);\n const lineBreakdown: BomLineCost[] = [];\n let totalCost = 0;\n let hasMissingCosts = false;\n\n for (const line of lines) {\n const effectiveQty = line.effectiveQtyPerUnit();\n const resolved = await this.resolveCost(line.componentSkuId);\n const unitCost = resolved ?? 0;\n const costUnavailable = resolved === null || resolved === undefined;\n const lineCost = unitCost * effectiveQty;\n if (costUnavailable) {\n hasMissingCosts = true;\n } else {\n totalCost += lineCost;\n }\n\n lineBreakdown.push({\n componentSkuId: line.componentSkuId,\n qtyPerUnit: Number(line.qtyPerUnit ?? 0),\n wastePercent: Number(line.wastePercent ?? 0),\n effectiveQty,\n unitCost,\n lineCost,\n uom: line.uom,\n costUnavailable,\n });\n }\n\n return {\n bomId,\n totalCost,\n currency: bom.currency || 'USD',\n lineBreakdown,\n hasMissingCosts,\n };\n }\n\n /**\n * Roll up the estimated labour to build one unit from the bill's routing:\n * total minutes, plus cost when a `rateResolver` was supplied (hourly rate\n * times minutes, per step). A bill with no routing returns an empty\n * estimate with zero totals, so callers can add it to the material cost\n * unconditionally. Retired operations still count: the routing names them.\n *\n * With `{ levels }` above 1, every sub-assembly the walk opens adds its own\n * bill's routing, multiplied by the units of it one unit needs (waste\n * included); see {@link LabourEstimate.subAssemblies}.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist.\n */\n async computeLabourEstimate(\n bomId: string,\n options: ExplosionOptions = {},\n ): Promise<LabourEstimate> {\n if (isMultiLevel(options.levels))\n return this.computeMultiLevelLabour(bomId, options);\n return this.labourFor(await this.requireBom(bomId));\n }\n\n /** One bill's own routing estimate, per unit of its product. */\n private async labourFor(bom: BillOfMaterials): Promise<LabourEstimate> {\n const bomId = bom.id as string;\n // The bill's own tenant's routing and operations, and global ones: a\n // shared global sub-assembly keeps its routing under a tenant context.\n const owner = bom.tenantId ?? null;\n const routing = (\n await readOwnAndGlobal(owner, () => this.routingSteps.findByBom(bomId))\n ).filter((step) => isOwnOrGlobal(step, owner));\n const steps: RoutingStepEstimate[] = [];\n let totalMinutes = 0;\n let totalCost = 0;\n let hasMissingRates = false;\n\n for (const step of routing) {\n const [operation] = (\n await readOwnAndGlobal(owner, () =>\n this.operations.list({ where: { id: step.operationId }, limit: 1 }),\n )\n ).filter((row) => isOwnOrGlobal(row, owner));\n const minutes = Number(step.estimatedMinutes ?? 0);\n const rate = operation ? await this.resolveRate(operation) : null;\n const rateUnavailable = rate === null;\n const stepCost = rate === null ? 0 : (minutes / 60) * rate;\n totalMinutes += minutes;\n if (rateUnavailable) hasMissingRates = true;\n else totalCost += stepCost;\n steps.push({\n stepId: step.id as string,\n sequence: step.sequence,\n operationId: step.operationId,\n operationCode: operation?.code ?? '',\n operationName: operation?.name ?? '',\n estimatedMinutes: minutes,\n hourlyRate: rate ?? 0,\n stepCost,\n rateUnavailable,\n });\n }\n\n return {\n bomId,\n hasRouting: steps.length > 0,\n totalMinutes,\n totalCost,\n currency: bom.currency || 'USD',\n steps,\n hasMissingRates,\n };\n }\n\n /**\n * Return a \"shopping list\" of materials needed to produce `qty` units of\n * the parent product against the given BOM. Lines that reference the\n * same component SKU are summed so each `componentSkuId` appears once\n * in the result.\n *\n * Does NOT mutate stock — purely a planning helper. Use\n * {@link ProductionService.consumeMaterials} when you're ready to write\n * stock movements.\n *\n * With `{ levels }` above 1, sub-assemblies with an active bill are\n * replaced by what their bills need, waste compounding down the levels;\n * this is {@link explode}'s `totals`.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist; throws a\n * plain `Error` when `qty` is not a positive finite number.\n */\n async explodeRequirements(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<MaterialRequirement[]> {\n if (isMultiLevel(options.levels))\n return (await this.explode(bomId, qty, options)).totals;\n assertPositiveQty(qty, 'explodeRequirements');\n const lines = await this.linesOf(await this.requireBom(bomId));\n\n // Sum across duplicate component SKUs. Keep the first-seen `uom` per\n // SKU; if two lines disagree on `uom`, we surface the first one and\n // assume the caller noticed during BOM authoring.\n const aggregate = new Map<string, MaterialRequirement>();\n for (const line of lines) {\n const effectiveQty = line.effectiveQtyPerUnit() * qty;\n const existing = aggregate.get(line.componentSkuId);\n if (existing) {\n existing.totalQty += effectiveQty;\n } else {\n aggregate.set(line.componentSkuId, {\n componentSkuId: line.componentSkuId,\n totalQty: effectiveQty,\n uom: line.uom,\n });\n }\n }\n\n return Array.from(aggregate.values());\n }\n\n /**\n * Check whether the requirements for producing `qty` units against the\n * given BOM are currently satisfied by available stock. Returns\n * `{ ok: true, shortages: [] }` when every component has enough\n * `available` stock across all locations; `{ ok: false, shortages: [...] }`\n * with one entry per insufficient component otherwise.\n *\n * Available stock is summed across every location (the planning\n * question is \"do we have it at all?\"; the operational question of\n * \"where do we pull from?\" is left to the caller of\n * {@link ProductionService.consumeMaterials}).\n *\n * With `{ levels }` above 1 the check is {@link planRequirements}: a\n * sub-assembly that is short and has an active bill is opened for its\n * shortfall, and each shortage is reported on the line where it occurs,\n * with its `level` and `path`. Stock is allocated as the walk goes, so a\n * component used in two places is not counted twice.\n *\n * Throws {@link BomNotFoundError} when the BOM does not exist.\n */\n async canProduce(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<CanProduceResult> {\n if (isMultiLevel(options.levels)) {\n const plan = await this.planRequirements(bomId, qty, options);\n return plan.ok\n ? { ok: true, shortages: [] }\n : { ok: false, shortages: plan.shortages };\n }\n const requirements = await this.explodeRequirements(bomId, qty);\n\n // Fan the per-component availability queries out in parallel so a\n // BOM with N components costs O(1) round-trips of latency instead\n // of O(N). `totalForSku` is a single aggregate read per component\n // — independent across SKUs — so there's no ordering or\n // consistency concern from running them concurrently.\n const availabilities = await Promise.all(\n requirements.map(async (requirement) => ({\n requirement,\n available: await this.stockService.levels.totalForSku(\n requirement.componentSkuId,\n 'available',\n ),\n })),\n );\n\n const shortages: MaterialShortage[] = [];\n for (const { requirement, available } of availabilities) {\n if (available < requirement.totalQty) {\n shortages.push({\n componentSkuId: requirement.componentSkuId,\n requested: requirement.totalQty,\n available,\n });\n }\n }\n\n if (shortages.length === 0) {\n return { ok: true, shortages: [] };\n }\n return { ok: false, shortages };\n }\n\n /**\n * Walk the bill for `qty` units and return every line, depth first, with\n * its level, the path of bills above it and its quantity for the run, plus\n * the rolled-up `totals`. Gross requirements: stock is not read.\n *\n * `levels` (default `1`) says how far to open sub-assemblies; see\n * {@link ExplosionLevels}. Waste compounds: a sub-assembly line's quantity\n * includes its own waste, and its bill's lines are multiplied by it.\n *\n * Throws {@link BomNotFoundError}, `BomStructureCycleError` when stored\n * data already has a product containing itself, `BomExplosionLimitError`\n * past the depth or line limits, and a plain `Error` for a bad `qty` or\n * `levels`.\n */\n async explode(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<Explosion> {\n assertPositiveQty(qty, 'explode');\n const levels = options.levels ?? 1;\n const top = await this.requireBom(bomId);\n const walk = new StructureWalk(this.assemblies, top, levels);\n const lines: ExplodedLine[] = [];\n\n const visit = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n parentQty: number,\n ): Promise<void> => {\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const sub = walk.open(component, level, path);\n const exploded = explodedLine(line, component, level, path, parentQty);\n exploded.expanded = sub !== null;\n lines.push(exploded);\n if (sub)\n await visit(sub.bomId, level + 1, [...path, sub], exploded.totalQty);\n }\n };\n await visit(bomId, 1, [await walk.root()], qty);\n\n return {\n bomId,\n productId: top.productId,\n qty,\n levels,\n lines,\n totals: sumRequirements(lines.filter((line) => !line.expanded)),\n };\n }\n\n /**\n * Net the walk against stock: for each line, the available stock (all\n * locations) not yet allocated to an earlier line and how much is short.\n * A sub-assembly that is short and has an active bill is opened for the\n * shortfall only, so its lines say what building that shortfall takes;\n * one covered by stock is not opened. Lines that are short and cannot be\n * opened are the `shortages`, each at its own level.\n *\n * These are facts, not a decision: the application decides whether a\n * short sub-assembly is built or bought. Does not mutate stock.\n *\n * Throws as {@link explode} does.\n */\n async planRequirements(\n bomId: string,\n qty: number,\n options: ExplosionOptions = {},\n ): Promise<RequirementsPlan> {\n assertPositiveQty(qty, 'planRequirements');\n const levels = options.levels ?? 1;\n const top = await this.requireBom(bomId);\n const walk = new StructureWalk(this.assemblies, top, levels);\n const lines: PlannedLine[] = [];\n const shortages: MaterialShortage[] = [];\n const unallocated = new Map<string, Promise<number>>();\n const tenantId = top.tenantId ?? null;\n const availableFor = (skuId: string): Promise<number> => {\n let pending = unallocated.get(skuId);\n if (!pending) {\n pending = this.availableInTenant(skuId, tenantId);\n unallocated.set(skuId, pending);\n }\n return pending;\n };\n\n const visit = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n parentQty: number,\n ): Promise<void> => {\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const base = explodedLine(line, component, level, path, parentQty);\n const available = await availableFor(line.componentSkuId);\n const allocated = Math.min(available, base.totalQty);\n unallocated.set(\n line.componentSkuId,\n Promise.resolve(available - allocated),\n );\n // Rounded to the quantity precision: `2.2 * 100` against 220 in\n // stock is not short.\n const short = Math.max(0, roundQuantity(base.totalQty - allocated));\n const sub = short > 0 ? walk.open(component, level, path) : null;\n lines.push({ ...base, expanded: sub !== null, available, short });\n if (sub) {\n await visit(sub.bomId, level + 1, [...path, sub], short);\n } else if (short > 0) {\n shortages.push({\n componentSkuId: line.componentSkuId,\n requested: base.totalQty,\n available,\n level,\n path,\n uom: base.uom,\n buildable: component.buildable,\n });\n }\n }\n };\n await visit(bomId, 1, [await walk.root()], qty);\n\n return {\n bomId,\n productId: top.productId,\n qty,\n levels,\n lines,\n shortages,\n ok: shortages.length === 0,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Internal helpers\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Fetch a BOM by id or throw {@link BomNotFoundError}. Centralises the\n * \"missing BOM\" error path so callers don't have to repeat the check.\n */\n /**\n * A bill's lines: its own tenant's and global ones, whatever tenant\n * context is active (a context alone would hide a global bill's lines).\n */\n private async linesOf(bom: BillOfMaterials): Promise<BomLine[]> {\n const owner = bom.tenantId ?? null;\n return (\n await readOwnAndGlobal(owner, () =>\n this.lines.findByBom(bom.id as string),\n )\n ).filter((line) => isOwnOrGlobal(line, owner));\n }\n\n private async requireBom(bomId: string): Promise<BillOfMaterials> {\n if (!bomId) throw new BomNotFoundError(bomId);\n // Under a tenant context a global bill is hidden from `get`; read the\n // tenant's bills and global ones (another tenant's never).\n const tenantId = getCurrentTenant()?.tenantId;\n const bom = tenantId\n ? ((\n await readOwnAndGlobal(tenantId, () =>\n this.boms.list({ where: { id: bomId }, limit: 1 }),\n )\n ).find((row) => isOwnOrGlobal(row, tenantId)) ?? null)\n : await this.boms.get(bomId);\n if (!bom) throw new BomNotFoundError(bomId);\n return bom;\n }\n\n /**\n * Available stock of a SKU across locations, counting only the top bill's\n * tenant's rows and global ones, whatever tenant context is active (the\n * walk reads its structure the same way).\n */\n private async availableInTenant(\n skuId: string,\n tenantId: string | null,\n ): Promise<number> {\n const rows = await readOwnAndGlobal(tenantId, () =>\n this.stockService.levels.list({ where: { skuId, state: 'available' } }),\n );\n let total = 0;\n for (const row of rows)\n if (isOwnOrGlobal(row, tenantId)) total += Number(row.qty ?? 0);\n return Math.max(0, total);\n }\n\n /** {@link computeMaterialCost} with `levels` above 1. */\n private async computeMultiLevelCost(\n bomId: string,\n options: ExplosionOptions,\n ): Promise<BomCostRollup> {\n const top = await this.requireBom(bomId);\n const currency = top.currency || 'USD';\n const walk = new StructureWalk(this.assemblies, top, options.levels ?? 1);\n\n const rollUp = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n ): Promise<Omit<BomCostRollup, 'bomId' | 'currency'>> => {\n const lineBreakdown: BomLineCost[] = [];\n let totalCost = 0;\n let hasMissingCosts = false;\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const sub = walk.open(component, level, path);\n const effectiveQty = line.effectiveQtyPerUnit();\n const entry: BomLineCost = {\n componentSkuId: line.componentSkuId,\n qtyPerUnit: Number(line.qtyPerUnit ?? 0),\n wastePercent: Number(line.wastePercent ?? 0),\n effectiveQty,\n unitCost: 0,\n lineCost: 0,\n uom: line.uom,\n costUnavailable: false,\n };\n // A sub-assembly bill in another currency cannot be added to this\n // total; its line is priced through the resolver, as an unopened one.\n const sameCurrency =\n (component.activeBom?.currency || 'USD') === currency;\n if (sub && sameCurrency) {\n const inner = await rollUp(sub.bomId, level + 1, [...path, sub]);\n entry.unitCost = inner.totalCost;\n entry.lineCost = inner.totalCost * effectiveQty;\n entry.costUnavailable = inner.hasMissingCosts;\n entry.subBomId = sub.bomId;\n entry.components = inner.lineBreakdown;\n totalCost += entry.lineCost;\n } else {\n const resolved = await this.resolveCost(line.componentSkuId);\n entry.unitCost = resolved ?? 0;\n entry.lineCost = entry.unitCost * effectiveQty;\n entry.costUnavailable = resolved === null;\n if (!entry.costUnavailable) totalCost += entry.lineCost;\n }\n if (entry.costUnavailable) hasMissingCosts = true;\n lineBreakdown.push(entry);\n }\n return { totalCost, lineBreakdown, hasMissingCosts };\n };\n\n const rolled = await rollUp(bomId, 1, [await walk.root()]);\n return { bomId, currency, ...rolled };\n }\n\n /** {@link computeLabourEstimate} with `levels` above 1. */\n private async computeMultiLevelLabour(\n bomId: string,\n options: ExplosionOptions,\n ): Promise<LabourEstimate> {\n const top = await this.requireBom(bomId);\n const walk = new StructureWalk(this.assemblies, top, options.levels ?? 1);\n const own = await this.labourFor(top);\n const estimates = new Map<string, Promise<LabourEstimate>>();\n const subAssemblies: SubAssemblyLabour[] = [];\n\n const visit = async (\n billId: string,\n level: number,\n path: ExplosionPathEntry[],\n units: number,\n ): Promise<void> => {\n for (const line of await walk.linesOf(billId)) {\n const component = await walk.resolve(line.componentSkuId);\n const sub = walk.open(component, level, path);\n if (!sub || !component.activeBom) continue;\n const subBill = component.activeBom;\n let estimate = estimates.get(sub.bomId);\n if (!estimate) {\n estimate = this.labourFor(subBill);\n estimates.set(sub.bomId, estimate);\n }\n const unitsPerUnit = units * line.effectiveQtyPerUnit();\n subAssemblies.push({\n lineId: line.id as string,\n componentSkuId: line.componentSkuId,\n name: sub.name,\n level,\n path,\n unitsPerUnit,\n estimate: await estimate,\n });\n await visit(sub.bomId, level + 1, [...path, sub], unitsPerUnit);\n }\n };\n await visit(bomId, 1, [await walk.root()], 1);\n\n let totalMinutes = own.totalMinutes;\n let totalCost = own.totalCost;\n let hasMissingRates = own.hasMissingRates;\n for (const sub of subAssemblies) {\n totalMinutes += sub.unitsPerUnit * sub.estimate.totalMinutes;\n // Cost in another currency is not added; the total is then a lower\n // bound, as with a missing rate.\n if (sub.estimate.currency === own.currency) {\n totalCost += sub.unitsPerUnit * sub.estimate.totalCost;\n if (sub.estimate.hasMissingRates) hasMissingRates = true;\n } else if (sub.estimate.hasRouting) {\n hasMissingRates = true;\n }\n }\n return {\n ...own,\n hasRouting:\n own.hasRouting || subAssemblies.some((sub) => sub.estimate.hasRouting),\n totalMinutes,\n totalCost,\n hasMissingRates,\n subAssemblies,\n };\n }\n\n /** Run the {@link OperationRateResolver}; `null` when there is no rate. */\n private async resolveRate(\n operation: Parameters<OperationRateResolver>[0],\n ): Promise<number | null> {\n if (!this.rateResolver) return null;\n const value = await this.rateResolver(operation);\n if (value === null || value === undefined || !Number.isFinite(value))\n return null;\n return Number(value);\n }\n\n /**\n * Run the supplied {@link ComponentCostResolver} for the given component\n * SKU. Returns `null` when no resolver is registered or when the\n * resolver returns `null` / `undefined`.\n */\n private async resolveCost(componentSkuId: string): Promise<number | null> {\n if (!this.costResolver) return null;\n const value = await this.costResolver(componentSkuId);\n if (value === null || value === undefined) return null;\n if (!Number.isFinite(value)) return null;\n return Number(value);\n }\n}\n\n/**\n * One line of a walk before stock is considered: quantities for the run,\n * with the parent's quantity (which already carries every waste above it)\n * times this line's quantity per unit including its own waste.\n */\nfunction explodedLine(\n line: BomLine,\n component: ResolvedComponent,\n level: number,\n path: ExplosionPathEntry[],\n parentQty: number,\n): ExplodedLine {\n const effectiveQtyPerUnit = line.effectiveQtyPerUnit();\n return {\n lineId: line.id as string,\n bomId: line.bomId,\n level,\n path,\n componentSkuId: line.componentSkuId,\n kind: component.kind,\n name: component.product?.name ?? '',\n skuCode: component.sku?.code ?? '',\n qtyPerUnit: Number(line.qtyPerUnit ?? 0),\n wastePercent: Number(line.wastePercent ?? 0),\n effectiveQtyPerUnit,\n uom: line.uom,\n totalQty: parentQty * effectiveQtyPerUnit,\n buildable: component.buildable,\n subBomId: component.activeBom?.id ?? null,\n expanded: false,\n };\n}\n\n/**\n * Sum lines per component SKU, keeping the first unit seen (as the\n * single-level explosion does).\n */\nfunction sumRequirements(lines: ExplodedLine[]): MaterialRequirement[] {\n const totals = new Map<string, MaterialRequirement>();\n for (const line of lines) {\n const existing = totals.get(line.componentSkuId);\n if (existing) existing.totalQty += line.totalQty;\n else\n totals.set(line.componentSkuId, {\n componentSkuId: line.componentSkuId,\n totalQty: line.totalQty,\n uom: line.uom,\n });\n }\n return Array.from(totals.values());\n}\n\n/**\n * Reject zero / negative / non-finite quantities up front for service\n * methods that accept production-run quantities.\n */\nfunction assertPositiveQty(qty: number, op: string): void {\n if (!Number.isFinite(qty) || qty <= 0) {\n throw new Error(`${op}: qty must be a positive finite number (got ${qty})`);\n }\n}\n\n/**\n * Build a `StockService` from a `db` config in the options. Called only\n * when the caller did not pass a pre-built `stockService` — the option\n * union guarantees `db` is present in that case, but TypeScript can't\n * narrow the discriminated union from a `??` value check, so this helper\n * validates at runtime with a clear message.\n */\nasync function buildStockService(\n options: BomServiceOptions,\n): Promise<StockService> {\n if (!options.db) {\n throw new Error(\n 'BomService.create: either `db` or `stockService` is required',\n );\n }\n return createStockService({ db: options.db });\n}\n\n/**\n * Convenience factory. Returns a fully-initialized {@link BomService}.\n */\nexport async function createBomService(\n options: BomServiceOptions,\n): Promise<BomService> {\n return BomService.create(options);\n}\n\n// Re-export the BomLine type alias to ease typing in adjacent code that\n// wires `BomLine[]` payloads through this service.\nexport type { BomLine };\n","/**\n * ProductionService — the operational bridge from a production order to\n * actual stock-mutating consume/produce events.\n *\n * Production orders themselves live in `@happyvertical/smrt-commerce` as a\n * `Contract` STI subtype. This service does NOT modify those rows; it\n * simply reads the order's `productId`, looks up the active BOM, and\n * writes the right stock movements through {@link StockService}.\n *\n * Three methods:\n *\n * - {@link consumeMaterials} — for each BOM line, deduct\n * `qtyPerUnit * (1 + wastePercent / 100) * runQty` from `available` at\n * the production location. Stamps every movement with\n * `sourceType: 'ProductionOrder'` + the production order id so audit\n * queries can roll them up later. Atomic across BOM lines (one tx).\n * - {@link produceFinishedGoods} — receive `runQty` of the finished\n * product's SKU into `available` at the production location. The caller\n * supplies the SKU id (the production order references a *product*, not\n * a *SKU*; in practice the production-order-level \"what we are making\"\n * gets resolved to a specific SKU per variant on the line items).\n * - {@link runProduction} — consume materials AND receive finished\n * goods in one transaction. Use this when consume + produce should be\n * jointly atomic (e.g. a make-to-stock workflow where the factory step\n * is invisible). Calling `consumeMaterials` then `produceFinishedGoods`\n * separately is NOT jointly atomic — each opens its own transaction\n * and a failure between them can leave materials deducted with no\n * finished SKU receipt to balance it.\n *\n * ## Location convention (explicit-arg design)\n *\n * The original design considered carrying `originLocationId` on a per-\n * package mixin of `ProductionOrder`, but that adds a meta field to a\n * model owned by a sibling package. The cleaner design — and the one\n * shipped here — keeps the location explicit on the call:\n *\n * ```typescript\n * await production.consumeMaterials(order, { locationId, qty });\n * await production.produceFinishedGoods(order, { locationId, qty, finishedSkuId });\n * ```\n *\n * Callers that want a \"default factory\" convention can stash the\n * `locationId` on their own production-order helper or wrap this service\n * with a thin domain-level façade.\n *\n * @packageDocumentation\n */\n\nimport type { DatabaseConfig } from '@happyvertical/smrt-core';\nimport type {\n StockMovementReason,\n StockMutationOptions,\n StockService,\n} from '@happyvertical/smrt-inventory';\nimport { createStockService } from '@happyvertical/smrt-inventory';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { BomLineCollection } from '../collections/BomLineCollection.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { BomLine } from '../models/BomLine.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport { BomNotFoundError, NoActiveBomForProductError } from '../types.js';\n\n/**\n * Minimal shape of a production order this service needs. We deliberately\n * accept this structural type rather than importing the `ProductionOrder`\n * class from `@happyvertical/smrt-commerce` — that would either pull a\n * dep on commerce into manufacturing or force commerce to extend a\n * manufacturing interface. Plain duck typing keeps the dependency arrow\n * pointing the way the architecture asks for (manufacturing builds on\n * inventory; commerce stays orthogonal).\n */\nexport interface ProductionOrderRef {\n /** Production order row id. Used for `sourceId` on every movement. */\n id?: string | null;\n /**\n * Plain string reference to the upstream product the order is asked to\n * produce. Used to look up the active BOM for the consume step.\n */\n productId?: string;\n /**\n * Optional explicit BOM id. When set, {@link consumeMaterials} uses this\n * row even if the product has a different `active` BOM — useful when an\n * order was kicked off against a specific revision.\n */\n bomId?: string;\n}\n\n/**\n * Per-call options for {@link ProductionService.consumeMaterials}.\n */\nexport interface ConsumeMaterialsOptions {\n /**\n * Inventory location id (warehouse / factory) where the materials are\n * pulled from. Required because the production order itself does not\n * carry a stocking location — see \"Location convention\" in the module\n * doc.\n */\n locationId: string;\n /**\n * Run quantity — how many finished units the BOM is being multiplied\n * by. Each BOM line's `effectiveQtyPerUnit()` is multiplied by this\n * number before being deducted from `available` stock.\n */\n qty: number;\n /**\n * Optional override of the reason code stamped on each\n * {@link StockMovement}. Defaults to `'production_consume'`.\n */\n reasonCode?: StockMovementReason;\n /**\n * Optional free-form note attached to every emitted movement.\n */\n note?: string;\n /**\n * Optional override of the `sourceType` stamped on each movement, with\n * the order's `id` as `sourceId`. Defaults to `'ProductionOrder'`;\n * `ProductionRunService` passes `'ProductionRunCompletion'`.\n */\n sourceType?: string;\n}\n\n/**\n * Per-call options for {@link ProductionService.produceFinishedGoods}.\n */\nexport interface ProduceFinishedGoodsOptions {\n /**\n * Inventory location id (warehouse / factory) where the finished goods\n * land. Required for the same reason as {@link ConsumeMaterialsOptions.locationId}.\n */\n locationId: string;\n /**\n * Quantity of finished goods produced. Always positive.\n */\n qty: number;\n /**\n * The SKU id of the finished product to receive into `available`. The\n * production order references a `productId` (a `Product` from\n * `@happyvertical/smrt-products`), but a product typically has multiple\n * SKUs (one per variant). The caller decides which concrete SKU each\n * line of the run produces.\n */\n finishedSkuId: string;\n /**\n * Optional override of the reason code stamped on the\n * {@link StockMovement}. Defaults to `'production_produce'`.\n */\n reasonCode?: StockMovementReason;\n /**\n * Optional free-form note attached to the emitted movement.\n */\n note?: string;\n /**\n * Optional override of the `sourceType` stamped on the movement. Defaults\n * to `'ProductionOrder'`; see {@link ConsumeMaterialsOptions.sourceType}.\n */\n sourceType?: string;\n}\n\n/**\n * Options accepted by {@link ProductionService.create}.\n *\n * Either pass a `db` for the service to construct its own\n * collections + {@link StockService}, or share a pre-built `stockService`\n * across subsystems.\n */\nexport type ProductionServiceOptions =\n | { db: DatabaseConfig; stockService?: StockService }\n | { stockService: StockService; db?: DatabaseConfig };\n\n/**\n * A single emitted stock movement, returned by\n * {@link ProductionService.consumeMaterials} so callers can audit / log\n * the impact of a production run without re-querying.\n */\nexport interface ConsumeResult {\n /** Component SKU id whose stock was deducted. */\n componentSkuId: string;\n /** Quantity deducted (already inflated for waste). */\n qty: number;\n /** Location the stock was deducted from. */\n locationId: string;\n}\n\n/**\n * Result of {@link ProductionService.produceFinishedGoods}.\n */\nexport interface ProduceResult {\n /** Finished SKU id whose stock increased. */\n finishedSkuId: string;\n /** Quantity received. */\n qty: number;\n /** Location the stock was received into. */\n locationId: string;\n}\n\n/**\n * Result of {@link ProductionService.runProduction} — the consume + produce\n * pair, run inside one transaction.\n */\nexport interface RunProductionResult {\n consumed: ConsumeResult[];\n produced: ProduceResult;\n}\n\n/**\n * Operational bridge between a production order and the inventory ledger.\n */\nexport class ProductionService {\n private constructor(\n /**\n * BOM collection bound to the OUTER (non-tx) database. Used for\n * pre-flight reads in `prepareConsume()` — those run before the\n * stock-service transaction opens, by design (read-then-tx keeps\n * the transaction window narrow and rollback only covers actual\n * mutations). DO NOT add BOM writes that depend on the stock-tx\n * rolling them back together — this collection is not tx-scoped\n * and writes here would commit independently. If joint BOM-plus-\n * stock mutations are ever needed, build tx-scoped BOM collections\n * inside the `stockService.withTransaction` callback (mirror the\n * pattern in StockService.withTransaction itself).\n */\n public readonly boms: BillOfMaterialsCollection,\n /**\n * BomLine collection bound to the OUTER database. Same caveat as\n * {@link boms} — pre-flight reads only.\n */\n public readonly lines: BomLineCollection,\n public readonly stockService: StockService,\n ) {}\n\n /** Factory — prefer {@link createProductionService}. */\n static async create(\n options: ProductionServiceOptions,\n ): Promise<ProductionService> {\n const stockService =\n options.stockService ?? (await buildStockService(options));\n // Share the StockService's db so production + stock writes ride one\n // connection/pool. StockService.db is exposed publicly for this case.\n const sharedDb = options.db ?? stockService.db;\n const [boms, lines] = await Promise.all([\n BillOfMaterialsCollection.create({ db: sharedDb }),\n BomLineCollection.create({ db: sharedDb }),\n ]);\n return new ProductionService(boms, lines, stockService);\n }\n\n /**\n * Walk every BOM line for the production order's BOM and deduct\n * `effectiveQtyPerUnit() * qty` from `available` at `locationId`. Each\n * deduction goes through {@link StockService.adjust} with `sourceType:\n * 'ProductionOrder'` and `sourceId: order.id`. Returns the list of\n * movements emitted so callers can log / surface them.\n *\n * **Atomic across lines**: all per-line deductions and their audit\n * movements run inside a single `stockService.withTransaction(...)`\n * scope. An `InsufficientStockError` on line N+1 rolls back lines 1..N\n * so production-order posting never leaves materials half-consumed.\n * Pre-flight with {@link BomService.canProduce} when you'd rather know\n * upfront than discover the shortfall mid-run.\n *\n * Throws {@link NoActiveBomForProductError} if no BOM id is supplied\n * on the order *and* no active BOM exists for the order's `productId`.\n * Throws {@link BomNotFoundError} if `order.bomId` is supplied but\n * doesn't resolve. Throws plain `Error` on missing `locationId`,\n * non-positive `qty`, or when both `order.bomId` and `order.productId`\n * are absent (a programmer error — neither input identifies a BOM).\n * Re-throws `InsufficientStockError` from `StockService.adjust` if a\n * line would drive `available` below zero.\n */\n async consumeMaterials(\n order: ProductionOrderRef,\n options: ConsumeMaterialsOptions,\n ): Promise<ConsumeResult[]> {\n const ctx = await this.prepareConsume(order, options);\n return this.stockService.withTransaction(async (tx) =>\n this.consumeMaterialsWith(tx, ctx),\n );\n }\n\n /**\n * Receive `qty` of the finished SKU into `available` at `locationId`.\n * Goes through {@link StockService.receive} with `sourceType:\n * 'ProductionOrder'` and `sourceId: order.id`. Returns a single\n * {@link ProduceResult} describing what landed.\n *\n * Throws plain `Error` on missing required arguments or non-positive\n * `qty`.\n */\n async produceFinishedGoods(\n order: ProductionOrderRef,\n options: ProduceFinishedGoodsOptions,\n ): Promise<ProduceResult> {\n const ctx = prepareProduce(order, options);\n return this.stockService.withTransaction(async (tx) =>\n this.produceFinishedGoodsWith(tx, ctx),\n );\n }\n\n /**\n * Atomically consume materials AND receive finished goods for one\n * production run.\n *\n * Both calls share a single `stockService.withTransaction(...)` scope:\n *\n * - Materials are deducted line-by-line via {@link StockService.adjust}.\n * - Finished goods are received via {@link StockService.receive}.\n * - If ANY step throws — a BOM-line shortage, the produce-leg adapter\n * failing, a tenancy mismatch surfaced by an interceptor — every\n * write in the transaction rolls back. The materials are restored\n * and the finished SKU's `available` row is unchanged. Callers\n * never observe \"materials deducted but finished goods missing\".\n *\n * Use this when consume + produce should be one indivisible event —\n * e.g. a make-to-stock workflow posting `production_order:completed`\n * where the shop floor step is invisible to the ledger.\n *\n * The two-call form (call {@link consumeMaterials} then {@link\n * produceFinishedGoods} separately) is still appropriate when the\n * factory step is a real wall-clock gap that other observers need to\n * see — work-in-progress dashboards, partial-run reporting, etc.\n *\n * Throws the same errors as the individual methods: missing args\n * (plain `Error`), unresolvable BOM ({@link BomNotFoundError} /\n * {@link NoActiveBomForProductError}), or `InsufficientStockError`\n * if a line would drive `available` below zero.\n */\n async runProduction(\n order: ProductionOrderRef,\n options: {\n consume: ConsumeMaterialsOptions;\n produce: ProduceFinishedGoodsOptions;\n },\n ): Promise<RunProductionResult> {\n const consumeCtx = await this.prepareConsume(order, options.consume);\n const produceCtx = prepareProduce(order, options.produce);\n return this.stockService.withTransaction(async (tx) => {\n const consumed = await this.consumeMaterialsWith(tx, consumeCtx);\n const produced = await this.produceFinishedGoodsWith(tx, produceCtx);\n return { consumed, produced };\n });\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Internal helpers\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Validate consume args, resolve the BOM + lines, and pre-compute the\n * mutation attribution. Anything that can throw without a tx open\n * happens here so the transaction we open later doesn't have to roll\n * back over a validation miss.\n */\n private async prepareConsume(\n order: ProductionOrderRef,\n options: ConsumeMaterialsOptions,\n ): Promise<ConsumeContext> {\n assertLocationId(options.locationId, 'consumeMaterials');\n assertPositiveQty(options.qty, 'consumeMaterials');\n const orderId = order.id ?? '';\n if (!orderId) {\n throw new Error(\n 'consumeMaterials: order.id is required for source attribution',\n );\n }\n const bom = await this.resolveBom(order);\n // The bill's own structure: its tenant's lines and global ones, never a\n // line another tenant wrote against this bill id (an unscoped read\n // would return those too).\n const owner = bom.tenantId ?? null;\n const lines = (\n await readOwnAndGlobal(owner, () => this.lines.findByBom(bom.id!))\n ).filter((line) => isOwnOrGlobal(line, owner));\n return {\n orderId,\n lines,\n runQty: options.qty,\n locationId: options.locationId,\n mutationOptions: {\n sourceType: options.sourceType ?? 'ProductionOrder',\n sourceId: orderId,\n reasonCode: options.reasonCode ?? 'production_consume',\n note: options.note,\n },\n };\n }\n\n /**\n * Execute the consume leg against a caller-supplied tx-bound\n * {@link StockService}. Public entry points (`consumeMaterials`,\n * `runProduction`) open the tx and call through here. Each per-line\n * `tx.adjust(...)` shares the same transaction so a shortfall on\n * line N+1 rolls back lines 1..N.\n */\n private async consumeMaterialsWith(\n tx: StockService,\n ctx: ConsumeContext,\n ): Promise<ConsumeResult[]> {\n const emitted: ConsumeResult[] = [];\n for (const line of ctx.lines) {\n const qty = line.effectiveQtyPerUnit() * ctx.runQty;\n if (qty <= 0) continue;\n await tx.adjust(\n line.componentSkuId,\n ctx.locationId,\n -qty,\n ctx.mutationOptions,\n );\n emitted.push({\n componentSkuId: line.componentSkuId,\n qty,\n locationId: ctx.locationId,\n });\n }\n return emitted;\n }\n\n /**\n * Execute the produce leg against a caller-supplied tx-bound\n * {@link StockService}. Public entry points (`produceFinishedGoods`,\n * `runProduction`) open the tx and call through here.\n */\n private async produceFinishedGoodsWith(\n tx: StockService,\n ctx: ProduceContext,\n ): Promise<ProduceResult> {\n await tx.receive(\n ctx.finishedSkuId,\n ctx.locationId,\n ctx.qty,\n ctx.mutationOptions,\n );\n return {\n finishedSkuId: ctx.finishedSkuId,\n qty: ctx.qty,\n locationId: ctx.locationId,\n };\n }\n\n /**\n * Resolve the BOM for a production order. Order of preference:\n *\n * 1. Explicit `order.bomId` if supplied — the caller pinned a specific\n * revision; we honor that pin or fail. We deliberately do NOT fall\n * back to the active BOM when `order.bomId` is stale or mistyped,\n * because silently switching recipes would have the production run\n * consume materials against a different BOM than the order locked.\n * 2. The active BOM for `order.productId`.\n *\n * Throws {@link BomNotFoundError} when an explicit `order.bomId` is\n * supplied but doesn't resolve to a row.\n *\n * Throws {@link NoActiveBomForProductError} when no `bomId` was supplied\n * and the product has no active BOM.\n *\n * Throws plain `Error` when neither `order.bomId` nor `order.productId`\n * is supplied — there's nothing to resolve against, so we surface that\n * as a programmer error with a distinct message rather than overload\n * `NoActiveBomForProductError` with an empty product id.\n */\n private async resolveBom(\n order: ProductionOrderRef,\n ): Promise<BillOfMaterials> {\n if (order.bomId) {\n const bomId = order.bomId;\n // Under a tenant context a global bill is hidden from `get`; read the\n // tenant's bills and global ones instead (another tenant's never).\n const tenantId = getCurrentTenant()?.tenantId;\n const bom = tenantId\n ? ((\n await readOwnAndGlobal(tenantId, () =>\n this.boms.list({ where: { id: bomId }, limit: 1 }),\n )\n ).find((row) => isOwnOrGlobal(row, tenantId)) ?? null)\n : await this.boms.get(bomId);\n if (!bom) throw new BomNotFoundError(bomId);\n return bom;\n }\n const productId = order.productId ?? '';\n if (!productId) {\n // The order carries neither an explicit `bomId` nor a `productId`,\n // so there is nothing for us to resolve against. Surface that as a\n // distinct, actionable error rather than constructing\n // `NoActiveBomForProductError('')` — the empty-string message\n // (\"No active Bill of Materials found for product: ...\") buries\n // the actual misuse (missing input field) under a SQL-flavoured\n // \"no row found\" error that doesn't reflect what went wrong.\n throw new Error(\n 'consumeMaterials: order.productId is required when order.bomId is not supplied',\n );\n }\n const active = await this.boms.findActiveForProduct(productId);\n if (!active) throw new NoActiveBomForProductError(productId);\n return active;\n }\n}\n\n/**\n * Pre-computed consume work that an open transaction can apply without\n * any further validation. Built by {@link ProductionService.prepareConsume}.\n */\ninterface ConsumeContext {\n orderId: string;\n lines: BomLine[];\n runQty: number;\n locationId: string;\n mutationOptions: StockMutationOptions;\n}\n\n/**\n * Pre-computed produce work that an open transaction can apply without\n * any further validation. Built by {@link prepareProduce}.\n */\ninterface ProduceContext {\n orderId: string;\n finishedSkuId: string;\n qty: number;\n locationId: string;\n mutationOptions: StockMutationOptions;\n}\n\n/**\n * Validate produce args and pre-compute the mutation attribution.\n * Mirrors {@link ProductionService.prepareConsume}; it's a free function\n * because the produce leg doesn't need access to any collection state.\n */\nfunction prepareProduce(\n order: ProductionOrderRef,\n options: ProduceFinishedGoodsOptions,\n): ProduceContext {\n assertLocationId(options.locationId, 'produceFinishedGoods');\n assertPositiveQty(options.qty, 'produceFinishedGoods');\n if (!options.finishedSkuId) {\n throw new Error(\n 'produceFinishedGoods: finishedSkuId is required (the production order references a productId; the caller picks which SKU is being produced)',\n );\n }\n const orderId = order.id ?? '';\n if (!orderId) {\n throw new Error(\n 'produceFinishedGoods: order.id is required for source attribution',\n );\n }\n return {\n orderId,\n finishedSkuId: options.finishedSkuId,\n qty: options.qty,\n locationId: options.locationId,\n mutationOptions: {\n sourceType: options.sourceType ?? 'ProductionOrder',\n sourceId: orderId,\n reasonCode: options.reasonCode ?? 'production_produce',\n note: options.note,\n },\n };\n}\n\nfunction assertPositiveQty(qty: number, op: string): void {\n if (!Number.isFinite(qty) || qty <= 0) {\n throw new Error(`${op}: qty must be a positive finite number (got ${qty})`);\n }\n}\n\nfunction assertLocationId(locationId: string, op: string): void {\n if (!locationId || typeof locationId !== 'string') {\n throw new Error(`${op}: locationId is required`);\n }\n}\n\n/**\n * Build a `StockService` from a `db` config in the options. Called only\n * when the caller did not pass a pre-built `stockService` — the option\n * union guarantees `db` is present in that case, but TypeScript can't\n * narrow the discriminated union from a `??` value check, so this helper\n * validates at runtime with a clear message.\n */\nasync function buildStockService(\n options: ProductionServiceOptions,\n): Promise<StockService> {\n if (!options.db) {\n throw new Error(\n 'ProductionService.create: either `db` or `stockService` is required',\n );\n }\n return createStockService({ db: options.db });\n}\n\n/**\n * Convenience factory. Returns a fully-initialized {@link ProductionService}.\n */\nexport async function createProductionService(\n options: ProductionServiceOptions,\n): Promise<ProductionService> {\n return ProductionService.create(options);\n}\n\n// Re-export the BomLine type so callers don't have to reach into the\n// models barrel just to type service results.\nexport type { BomLine };\n","/**\n * Opt-in DispatchBus subscribers that bridge upstream production-order\n * signals to {@link ProductionService}.\n *\n * **Off by default.** The package never auto-subscribes — consumer apps\n * call {@link installManufacturingDispatchHandlers} from their `smrt.ts`\n * wiring once they have decided they want a `production_order:posted`\n * emit to consume materials and produce finished goods automatically.\n *\n * Two paired toggles let consumers wire only the legs they want:\n *\n * - `installProductionPosted` (default `true`) — on `production_order:posted`,\n * call {@link ProductionService.consumeMaterials} once.\n * - `installProductionCompleted` (default `false`) — on `production_order:completed`,\n * call {@link ProductionService.produceFinishedGoods} once. Off by default\n * because not every shop emits a separate \"completed\" event; lots of\n * workflows expect consume + produce to happen atomically when the\n * order is posted. Set both flags to `true` to get the split lifecycle.\n *\n * @packageDocumentation\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type {\n DatabaseConfig,\n DispatchBus,\n DispatchHandler,\n DispatchMetadata,\n} from '@happyvertical/smrt-core';\nimport type { StockService } from '@happyvertical/smrt-inventory';\nimport {\n createProductionService,\n type ProductionService,\n} from './ProductionService.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Shape of a `production_order:posted` payload this handler expects.\n *\n * The producer (typically the application's smrt.ts wiring once a\n * `ProductionOrder` row transitions to the posted state) packs one\n * payload per posted order. The handler consumes materials for every\n * unit declared by `qty`, attributed to `('ProductionOrder', productionOrderId)`\n * on each emitted {@link StockMovement}.\n */\nexport interface ProductionOrderPostedPayload {\n /** Production order row id. Used for `sourceId` on every movement. */\n productionOrderId: string;\n /**\n * Plain string reference to the upstream product the order is asked to\n * produce. Used to resolve the active BOM at consume time.\n */\n productId: string;\n /**\n * Inventory location id (factory / warehouse) where materials are\n * pulled from.\n */\n locationId: string;\n /**\n * Run quantity. Each BOM line's effective qty is multiplied by this\n * before being deducted from `available`.\n */\n qty: number;\n /**\n * Optional explicit BOM id, e.g. when the order locked itself to a\n * specific BOM revision when it was created. Falls back to the active\n * BOM for `productId` if omitted.\n */\n bomId?: string;\n /**\n * Optional finished SKU id. When provided AND the\n * `installProductionPosted` handler is wired to also produce in one\n * shot (which only happens when `installProductionCompleted` is false\n * and `producedOnPosted` is true), this is the SKU to receive into\n * `available`.\n */\n finishedSkuId?: string;\n}\n\n/**\n * Shape of a `production_order:completed` payload this handler expects.\n *\n * Typically emitted when the factory signs off the run and finished\n * goods are physically available to ship. Only used when callers opt in\n * via `installProductionCompleted`.\n */\nexport interface ProductionOrderCompletedPayload {\n /** Production order row id. Used for `sourceId` on the receipt movement. */\n productionOrderId: string;\n /**\n * SKU id of the finished product to receive into `available`. Required.\n */\n finishedSkuId: string;\n /** Inventory location id where the finished goods land. */\n locationId: string;\n /** Quantity received. */\n qty: number;\n}\n\n/**\n * Options accepted by {@link installManufacturingDispatchHandlers}.\n *\n * Provide either a pre-built {@link ProductionService} (when sharing\n * across subsystems) or a `db` / `stockService` for the helper to\n * construct one on first use.\n */\nexport type InstallManufacturingDispatchHandlersOptions = {\n /** Bus to subscribe on. Typically the app-wide DispatchBus. */\n dispatchBus: DispatchBus;\n /**\n * When `true` (default), install the `production_order:posted` handler.\n */\n installProductionPosted?: boolean;\n /**\n * When `true` (default `false`), install the\n * `production_order:completed` handler. Off by default because many\n * workflows want consume+produce to happen on `posted`; opt in to the\n * split lifecycle when your shop emits a separate completion event.\n */\n installProductionCompleted?: boolean;\n /**\n * When `true` (default `false`) AND `installProductionCompleted` is\n * false, the `production_order:posted` handler also calls\n * {@link ProductionService.produceFinishedGoods} after consume —\n * handy for \"make-to-stock instantly\" workflows where the factory\n * step is invisible.\n *\n * Ignored when `installProductionCompleted` is `true` (in that case\n * production is the completed-handler's job).\n */\n producedOnPosted?: boolean;\n} & (\n | {\n productionService: ProductionService;\n db?: DatabaseConfig;\n stockService?: StockService;\n }\n | {\n stockService: StockService;\n db?: DatabaseConfig;\n productionService?: undefined;\n }\n | {\n db: DatabaseConfig;\n stockService?: undefined;\n productionService?: undefined;\n }\n);\n\n/**\n * Result handle returned by {@link installManufacturingDispatchHandlers}.\n * Call `dispose()` to detach the subscribers (mainly useful in tests and\n * on graceful shutdown).\n */\nexport interface InstalledManufacturingDispatchHandlers {\n /** Resolved ProductionService — useful for follow-up writes in the same scope. */\n productionService: ProductionService;\n /** Detach every installed subscriber. Idempotent. */\n dispose(): void;\n}\n\n/**\n * Subscribe production-order handlers to the given DispatchBus.\n *\n * @example\n * ```typescript\n * import { createDispatchBus } from '@happyvertical/smrt-core';\n * import { installManufacturingDispatchHandlers } from '@happyvertical/smrt-manufacturing';\n *\n * const bus = await createDispatchBus({ db });\n * const handlers = await installManufacturingDispatchHandlers({\n * dispatchBus: bus,\n * db,\n * producedOnPosted: true, // consume + produce in one shot\n * });\n * ```\n */\nexport async function installManufacturingDispatchHandlers(\n options: InstallManufacturingDispatchHandlersOptions,\n): Promise<InstalledManufacturingDispatchHandlers> {\n const {\n dispatchBus,\n installProductionPosted = true,\n installProductionCompleted = false,\n producedOnPosted = false,\n } = options;\n\n const productionService =\n options.productionService ?? (await buildProductionService(options));\n\n const installed: Array<{ pattern: string; handler: DispatchHandler }> = [];\n\n if (installProductionPosted) {\n const shouldProduceOnPosted =\n producedOnPosted && !installProductionCompleted;\n const handler: DispatchHandler = async (payload, metadata) => {\n await handleProductionPosted(\n productionService,\n payload as ProductionOrderPostedPayload,\n metadata,\n shouldProduceOnPosted,\n );\n };\n dispatchBus.on('production_order:posted', handler);\n installed.push({ pattern: 'production_order:posted', handler });\n }\n\n if (installProductionCompleted) {\n const handler: DispatchHandler = async (payload, metadata) => {\n await handleProductionCompleted(\n productionService,\n payload as ProductionOrderCompletedPayload,\n metadata,\n );\n };\n dispatchBus.on('production_order:completed', handler);\n installed.push({ pattern: 'production_order:completed', handler });\n }\n\n return {\n productionService,\n dispose() {\n for (const entry of installed) {\n dispatchBus.off(entry.pattern, entry.handler);\n }\n installed.length = 0;\n },\n };\n}\n\nasync function handleProductionPosted(\n productionService: ProductionService,\n payload: ProductionOrderPostedPayload | null | undefined,\n metadata: DispatchMetadata,\n shouldProduce: boolean,\n): Promise<void> {\n // Mirror the inventory-handler observability + strictness pattern:\n // producer contract drift goes silent without this warn (the\n // dispatch bus would just see a quick return and never surface\n // anything), and a non-string `productionOrderId` (e.g. a numeric\n // primary key coming from a Postgres source) would otherwise land\n // as a non-string `sourceId` on every emitted StockMovement —\n // breaking the audit-trail-by-source-id pattern.\n const postedReason = malformedProductionPayloadReason(payload);\n if (postedReason || !payload) {\n warnMalformedPayload(\n 'production_order:posted',\n payload,\n metadata,\n postedReason ?? 'payload is null/undefined',\n );\n return;\n }\n\n // If `producedOnPosted` is enabled, the produce leg is required — not\n // optional. Validate `finishedSkuId` before consuming materials so we\n // don't leave inventory depleted with no corresponding receipt. (Without\n // this guard the consume leg would land, the produce leg would silently\n // skip due to the falsy `finishedSkuId`, and the audit log would carry\n // production_consume movements but no matching production_produce.)\n if (shouldProduce && !payload.finishedSkuId) {\n throw new Error(\n `production_order:posted handler: producedOnPosted is enabled but ` +\n `payload.finishedSkuId is missing (productionOrderId=${payload.productionOrderId}). ` +\n `Either supply finishedSkuId on the event, or disable producedOnPosted ` +\n `and emit production_order:completed separately.`,\n );\n }\n\n const order = {\n id: payload.productionOrderId,\n productId: payload.productId,\n bomId: payload.bomId,\n };\n const consumeNote = metadata.source\n ? `auto-consume via ${metadata.source}`\n : undefined;\n\n // When producedOnPosted is on, route through runProduction so consume\n // and produce share a single transaction. The two calls below would\n // otherwise open separate transactions, and a process crash or adapter\n // error between them would leave materials deducted with no\n // finished-goods receipt to balance the audit ledger.\n if (shouldProduce && payload.finishedSkuId) {\n const produceNote = metadata.source\n ? `auto-produce via ${metadata.source}`\n : undefined;\n await productionService.runProduction(order, {\n consume: {\n locationId: payload.locationId,\n qty: payload.qty,\n note: consumeNote,\n },\n produce: {\n locationId: payload.locationId,\n qty: payload.qty,\n finishedSkuId: payload.finishedSkuId,\n note: produceNote,\n },\n });\n return;\n }\n\n // Consume-only path — the produce leg lives on the separate\n // `production_order:completed` event (or is not modeled at all in\n // this workflow). consumeMaterials is itself atomic across BOM lines.\n await productionService.consumeMaterials(order, {\n locationId: payload.locationId,\n qty: payload.qty,\n note: consumeNote,\n });\n}\n\nasync function handleProductionCompleted(\n productionService: ProductionService,\n payload: ProductionOrderCompletedPayload | null | undefined,\n metadata: DispatchMetadata,\n): Promise<void> {\n const completedReason = malformedProductionPayloadReason(payload);\n if (completedReason || !payload) {\n warnMalformedPayload(\n 'production_order:completed',\n payload,\n metadata,\n completedReason ?? 'payload is null/undefined',\n );\n return;\n }\n await productionService.produceFinishedGoods(\n { id: payload.productionOrderId },\n {\n locationId: payload.locationId,\n qty: payload.qty,\n finishedSkuId: payload.finishedSkuId,\n note: metadata.source ? `auto-produce via ${metadata.source}` : undefined,\n },\n );\n}\n\n/**\n * Determine whether a production-order payload (either\n * `:posted` or `:completed`) is structurally unusable and return a\n * concrete reason naming the specific field that failed validation.\n * Returns `null` when the top-level shape is acceptable. Mirrors the\n * inventory package's per-signal helpers so the log message names the\n * actual failure instead of a hardcoded generic.\n */\nfunction malformedProductionPayloadReason(\n payload: { productionOrderId?: unknown } | null | undefined,\n): string | null {\n if (!payload || typeof payload !== 'object') {\n return 'payload is null/undefined or not an object';\n }\n if (\n !payload.productionOrderId ||\n typeof payload.productionOrderId !== 'string'\n ) {\n return 'missing or non-string `productionOrderId` (required for audit-trail source attribution)';\n }\n return null;\n}\n\n/**\n * Surface dispatch payloads that don't match the documented shape.\n * Mirrors the inventory package's `warnMalformedPayload` so a\n * misshapen `production_order:*` event leaves a trace (signal name,\n * source attribution, specific reason, payload-key summary) instead\n * of vanishing into the dispatch bus's silent-success path. Without\n * this, producer drift only surfaces as \"stock didn't move\" /\n * \"audit log empty\" bugs a long way from the handler.\n */\nfunction warnMalformedPayload(\n signal: string,\n payload: unknown,\n metadata: DispatchMetadata,\n reason: string,\n): void {\n logger.warn(\n `[@happyvertical/smrt-manufacturing] dispatch handler ignored a ${signal} ` +\n `event with a malformed payload (${reason}). ` +\n `Source: ${metadata.source ?? '<unknown>'}; payload keys: ` +\n `${payload && typeof payload === 'object' ? Object.keys(payload).join(',') : typeof payload}`,\n );\n}\n\n/**\n * Build a `ProductionService` for the dispatch installer when no\n * pre-built service was supplied. The discriminated option union\n * guarantees one of `stockService` / `db` is set in that branch, but\n * TypeScript can't narrow from a `??` value check on a sibling property,\n * so we validate at runtime with a clear error message.\n */\nasync function buildProductionService(\n options: InstallManufacturingDispatchHandlersOptions,\n): Promise<ProductionService> {\n if (options.stockService) {\n // Pass `db` through when supplied alongside `stockService` so the\n // resulting ProductionService points its BOM collections at the\n // caller-supplied manufacturing database. Dropping `db` here would\n // make the BOM collections fall back to `stockService.db`, which\n // is the *inventory* connection in deployments where the two\n // packages target different databases. ProductionService.create\n // already accepts both fields together; the discriminated option\n // union just doesn't make the combined shape obvious.\n return createProductionService({\n stockService: options.stockService,\n ...(options.db ? { db: options.db } : {}),\n });\n }\n if (!options.db) {\n throw new Error(\n 'installManufacturingDispatchHandlers: one of `productionService`, `stockService`, or `db` is required',\n );\n }\n return createProductionService({ db: options.db });\n}\n","/**\n * OperationService — maintain the managed list of {@link Operation} rows:\n * define, rename, change category or required qualification, retire and\n * reinstate. Operations are never deleted.\n *\n * @packageDocumentation\n */\n\nimport type { DatabaseConfig } from '@happyvertical/smrt-core';\nimport { OperationCollection } from '../collections/OperationCollection.js';\nimport type { Operation } from '../models/Operation.js';\nimport {\n type DefineOperationInput,\n DuplicateOperationCodeError,\n InvalidOperationInputError,\n OperationNotFoundError,\n} from '../operation-types.js';\n\n/** Options accepted by {@link OperationService.create}. */\nexport interface OperationServiceOptions {\n db: DatabaseConfig;\n}\n\nexport class OperationService {\n private constructor(public readonly operations: OperationCollection) {}\n\n /** Factory — prefer {@link createOperationService}. */\n static async create(\n options: OperationServiceOptions,\n ): Promise<OperationService> {\n return new OperationService(\n await OperationCollection.create({ db: options.db }),\n );\n }\n\n /**\n * Add an operation. The code is trimmed and must be unique per tenant;\n * throws {@link DuplicateOperationCodeError} when it is taken (retired\n * operations keep their code).\n */\n async define(input: DefineOperationInput): Promise<Operation> {\n const code = input.code?.trim() ?? '';\n const name = input.name?.trim() ?? '';\n if (!code)\n throw new InvalidOperationInputError('Operation code is required.');\n if (!name)\n throw new InvalidOperationInputError('Operation name is required.');\n if (await this.operations.findByCode(code))\n throw new DuplicateOperationCodeError(code);\n try {\n // `create()` persists. A concurrent definer of the same code must fail\n // rather than overwrite, hence insert-only.\n return await this.operations.create({\n code,\n name,\n category: input.category?.trim() ?? '',\n requiredQualificationId: input.requiredQualificationId?.trim() || null,\n isActive: true,\n _insertOnly: true,\n });\n } catch (error) {\n if (await this.operations.findByCode(code))\n throw new DuplicateOperationCodeError(code);\n throw error;\n }\n }\n\n /** Fetch an operation or throw {@link OperationNotFoundError}. */\n async get(id: string): Promise<Operation> {\n const operation = id ? await this.operations.get(id) : null;\n if (!operation) throw new OperationNotFoundError(id);\n return operation;\n }\n\n /** List operations; retired ones only with `includeRetired`. */\n async list(options: { includeRetired?: boolean } = {}): Promise<Operation[]> {\n return this.operations.listOperations(options);\n }\n\n /** Change the display name. The code does not change. */\n async rename(id: string, name: string): Promise<Operation> {\n const next = name?.trim() ?? '';\n if (!next)\n throw new InvalidOperationInputError('Operation name is required.');\n const operation = await this.get(id);\n operation.name = next;\n await operation.save();\n return operation;\n }\n\n /** Change category and/or required qualification; an empty string clears. */\n async update(\n id: string,\n changes: { category?: string; requiredQualificationId?: string },\n ): Promise<Operation> {\n const operation = await this.get(id);\n if (changes.category !== undefined)\n operation.category = changes.category.trim();\n if (changes.requiredQualificationId !== undefined)\n operation.requiredQualificationId =\n changes.requiredQualificationId.trim() || null;\n await operation.save();\n return operation;\n }\n\n /** Retire: stays on history and routings, leaves pickers. Idempotent. */\n async retire(id: string): Promise<Operation> {\n return this.setActive(id, false);\n }\n\n /** Reinstate a retired operation. Idempotent. */\n async reinstate(id: string): Promise<Operation> {\n return this.setActive(id, true);\n }\n\n private async setActive(id: string, isActive: boolean): Promise<Operation> {\n const operation = await this.get(id);\n if (operation.isActive !== isActive) {\n operation.isActive = isActive;\n await operation.save();\n }\n return operation;\n }\n}\n\n/** Convenience factory. */\nexport async function createOperationService(\n options: OperationServiceOptions,\n): Promise<OperationService> {\n return OperationService.create(options);\n}\n","/**\n * ProductionRunService — create production runs and report work on them as\n * it happens: \"25 to build, 12 done\".\n *\n * Every write runs in one database transaction that first saves the run\n * row. That save is revision-guarded, so it takes the row's lock and a\n * concurrent writer's save fails with a revision conflict; the loser's whole\n * transaction is retried against the committed run. Two people reporting\n * completions at once therefore both count, and a run never goes past its\n * target.\n *\n * Recording a completion moves no stock unless the caller asks. With\n * `consume` and/or `produce`, it uses the {@link ProductionService} paths\n * inside the same transaction as the completion, so a stock shortfall rolls\n * the completion back too.\n *\n * @packageDocumentation\n */\n\nimport { type DatabaseConfig, resolveDatabase } from '@happyvertical/smrt-core';\nimport {\n createStockService,\n type StockService,\n} from '@happyvertical/smrt-inventory';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { ProductionRunCollection } from '../collections/ProductionRunCollection.js';\nimport { ProductionRunCompletionCollection } from '../collections/ProductionRunCompletionCollection.js';\nimport type { BillOfMaterials } from '../models/BillOfMaterials.js';\nimport type { ProductionRun } from '../models/ProductionRun.js';\nimport type { ProductionRunCompletion } from '../models/ProductionRunCompletion.js';\nimport {\n type CreateProductionRunInput,\n InvalidProductionRunInputError,\n ProductionRunNotFoundError,\n ProductionRunOverCompletionError,\n ProductionRunStateError,\n type RecordCompletionInput,\n type RecordCompletionResult,\n} from '../production-run-types.js';\nimport {\n fromMillionths,\n MAX_QUANTITY,\n roundQuantity,\n toMillionths,\n} from '../quantity.js';\nimport { isOwnOrGlobal, readOwnAndGlobal } from '../tenant-scope.js';\nimport { BomNotFoundError, NoActiveBomForProductError } from '../types.js';\nimport {\n type ConsumeResult,\n type ProduceResult,\n ProductionService,\n} from './ProductionService.js';\n\n/**\n * Options accepted by {@link ProductionRunService.create}. With a\n * `stockService`, its database is used for everything, so a completion and\n * its stock movements share one transaction.\n */\nexport type ProductionRunServiceOptions =\n | { db: DatabaseConfig; stockService?: undefined }\n | { stockService: StockService; db?: undefined };\n\ntype TransactionalDb = {\n transaction?: <T>(work: (txDb: unknown) => Promise<T>) => Promise<T>;\n};\n\n/** Collections bound to one open transaction. */\ninterface RunTransaction {\n stock: StockService;\n runs: ProductionRunCollection;\n completions: ProductionRunCompletionCollection;\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Statuses in which work can still be reported or the run changed. */\nconst OPEN_STATUSES = new Set(['planned', 'in_progress']);\n\n/**\n * Creates runs and records their progress. Construct with\n * {@link ProductionRunService.create} or {@link createProductionRunService}.\n *\n * @example\n * ```typescript\n * const runs = await ProductionRunService.create({ db });\n * const run = await runs.createRun({ productId: frame.id, targetQty: 25 });\n * await runs.recordCompletion(run.id!, { qty: 12 });\n * // run: 25 to build, 12 done, in_progress\n * ```\n */\nexport class ProductionRunService {\n private constructor(\n /** The stock service whose database every collection here shares. */\n public readonly stockService: StockService,\n /** Production runs (reads; writes go through this service). */\n public readonly runs: ProductionRunCollection,\n /** Completions (reads; writes go through this service). */\n public readonly completions: ProductionRunCompletionCollection,\n /** Bills, to pin a run's bill. */\n public readonly boms: BillOfMaterialsCollection,\n ) {}\n\n /** Factory — prefer {@link createProductionRunService}. */\n static async create(\n options: ProductionRunServiceOptions,\n ): Promise<ProductionRunService> {\n let stockService = options.stockService;\n if (!stockService) {\n if (!options.db)\n throw new Error(\n 'ProductionRunService.create: either `db` or `stockService` is required',\n );\n // Resolve once so the stock service and every collection share one\n // handle: a config such as `:memory:` would otherwise open a database\n // per collection.\n const resolved = (await resolveDatabase(\n options.db,\n )) as unknown as DatabaseConfig;\n stockService = await createStockService({ db: resolved });\n }\n const db = stockService.db;\n // Recording a completion is atomic only inside a transaction; refuse an\n // adapter that cannot open one rather than writing step by step.\n if (typeof (db as unknown as TransactionalDb).transaction !== 'function')\n throw new Error(\n 'ProductionRunService requires a database adapter with transaction() support.',\n );\n const [runs, completions, boms] = await Promise.all([\n ProductionRunCollection.create({ db }),\n ProductionRunCompletionCollection.create({ db }),\n BillOfMaterialsCollection.create({ db }),\n ]);\n return new ProductionRunService(stockService, runs, completions, boms);\n }\n\n /**\n * Create a `planned` run of `targetQty` units, pinned to `bomId`, or to the\n * active bill of `productId`.\n *\n * @throws {InvalidProductionRunInputError} for a target that is not a\n * positive number, or when neither `bomId` nor `productId` is given.\n * @throws {BomNotFoundError} when `bomId` does not exist.\n * @throws {NoActiveBomForProductError} when `productId` has no active bill.\n */\n async createRun(input: CreateProductionRunInput): Promise<ProductionRun> {\n const targetQty = positive(input.targetQty, 'targetQty');\n // Pinned to the active tenant's bills and global ones; without a tenant\n // context, global bills only. Another tenant's bill is never usable.\n const tenantId = getCurrentTenant()?.tenantId ?? null;\n let bom: BillOfMaterials | null = null;\n if (input.bomId) {\n const bomId = input.bomId;\n const found = UUID_PATTERN.test(bomId)\n ? await readOwnAndGlobal(tenantId, () =>\n this.boms.list({ where: { id: bomId }, limit: 1 }),\n )\n : [];\n bom = found.find((row) => isOwnOrGlobal(row, tenantId)) ?? null;\n if (!bom?.id) throw new BomNotFoundError(bomId);\n } else if (input.productId) {\n const productId = input.productId;\n // The tenant's own active bill first, then a global one.\n const active = (\n await readOwnAndGlobal(tenantId, () =>\n this.boms.list({\n where: { productId, status: 'active' },\n orderBy: 'version DESC',\n }),\n )\n ).filter((candidate) => isOwnOrGlobal(candidate, tenantId));\n bom =\n active.find((candidate) => (candidate.tenantId ?? null) === tenantId) ??\n active[0] ??\n null;\n if (!bom?.id) throw new NoActiveBomForProductError(input.productId);\n } else {\n throw new InvalidProductionRunInputError(\n 'A production run needs a bomId or a productId.',\n );\n }\n // `create()` persists; no second save. The run belongs to its bill's\n // tenant (or the active one, for a global bill).\n return this.runs.create({\n tenantId: bom.tenantId ?? tenantId,\n bomId: bom.id as string,\n targetQty,\n completedQty: 0,\n status: 'planned',\n });\n }\n\n /** The run, or `null` when no run has this id. */\n async get(runId: string): Promise<ProductionRun | null> {\n if (!UUID_PATTERN.test(runId)) return null;\n return this.runs.get({ id: runId });\n }\n\n /** A run's completions in the order they happened. */\n async listCompletions(runId: string): Promise<ProductionRunCompletion[]> {\n if (!UUID_PATTERN.test(runId)) return [];\n return this.completions.findByRun(runId);\n }\n\n /** Mark a `planned` run `in_progress`. */\n async start(runId: string): Promise<ProductionRun> {\n return this.change(runId, 'start', (run) => {\n if (run.status !== 'planned')\n throw new ProductionRunStateError(runId, run.status, 'start');\n run.status = 'in_progress';\n });\n }\n\n /** Mark an open run `done`, short of its target if work stopped early. */\n async finish(runId: string): Promise<ProductionRun> {\n return this.change(runId, 'finish', (run) => {\n run.status = 'done';\n });\n }\n\n /** Cancel an open run. Completions already reported stay. */\n async cancel(runId: string): Promise<ProductionRun> {\n return this.change(runId, 'cancel', (run) => {\n run.status = 'cancelled';\n });\n }\n\n /**\n * Change an open run's target. It may not go below what is already done;\n * setting it to exactly that finishes the run.\n *\n * @throws {InvalidProductionRunInputError} for a target that is not a\n * positive number or is below the completed quantity.\n */\n async setTarget(runId: string, targetQty: number): Promise<ProductionRun> {\n const target = positive(targetQty, 'targetQty');\n return this.change(runId, 'retarget', (run) => {\n // Whole millionths: exact, see quantity.ts.\n const completed = toMillionths(Number(run.completedQty));\n const goal = toMillionths(target);\n if (goal < completed)\n throw new InvalidProductionRunInputError(\n `The target cannot be below the ${fromMillionths(completed)} already done.`,\n );\n run.targetQty = target;\n if (completed > 0 && completed >= goal) run.status = 'done';\n });\n }\n\n /**\n * Report `qty` finished units on an open run: records a dated completion,\n * adds it to `completedQty`, moves a `planned` run to `in_progress`, and\n * marks the run `done` when the target is reached. With `consume` and/or\n * `produce`, also writes the stock movements through\n * {@link ProductionService}, stamped `sourceType: 'ProductionRunCompletion'`\n * with the completion id. Everything runs in one transaction.\n *\n * Consumption takes the run's bill's own lines for `qty` units: a\n * sub-assembly on the bill is taken from stock as a component. Building\n * that sub-assembly is its own run.\n *\n * @throws {ProductionRunNotFoundError}\n * @throws {ProductionRunStateError} when the run is `done` or `cancelled`.\n * @throws {ProductionRunOverCompletionError} when `qty` is more than the\n * run has left.\n * @throws {InvalidProductionRunInputError} for a bad `qty` or date.\n * @throws `InsufficientStockError` from inventory when consuming would\n * drive stock below zero; nothing is recorded then.\n */\n async recordCompletion(\n runId: string,\n input: RecordCompletionInput,\n ): Promise<RecordCompletionResult> {\n const qty = positive(input.qty, 'qty');\n const completedAt =\n input.completedAt === undefined\n ? new Date()\n : new Date(input.completedAt);\n if (Number.isNaN(completedAt.getTime()))\n throw new InvalidProductionRunInputError(\n 'completedAt is not a valid date.',\n );\n\n return this.inTransaction(async (tx) => {\n const run = await this.load(tx, runId);\n if (!OPEN_STATUSES.has(run.status))\n throw new ProductionRunStateError(runId, run.status, 'complete');\n // Whole millionths, so every report counts exactly (quantity.ts).\n const target = toMillionths(Number(run.targetQty));\n const done = toMillionths(Number(run.completedQty));\n const reported = toMillionths(qty);\n const remaining = Math.max(0, target - done);\n if (reported > remaining)\n throw new ProductionRunOverCompletionError(\n runId,\n qty,\n fromMillionths(remaining),\n );\n const total = done + reported;\n run.completedQty = fromMillionths(total);\n run.status = total >= target ? 'done' : 'in_progress';\n // Revision-guarded: a concurrent report fails here and is retried.\n await run.save();\n // `create()` persists; no second save.\n const completion = await tx.completions.create({\n tenantId: run.tenantId ?? null,\n runId,\n qty,\n completedAt,\n });\n\n let consumed: ConsumeResult[] = [];\n let produced: ProduceResult | null = null;\n // Stock reads and writes in the run's tenant, whatever the caller's\n // context: the bill's lines and the levels are that tenant's.\n const inRunTenant = <T>(work: () => Promise<T>): Promise<T> =>\n run.tenantId ? withTenant({ tenantId: run.tenantId }, work) : work();\n if (input.consume || input.produce) {\n // Bound to the open transaction: its own withTransaction joins it.\n const production = await ProductionService.create({\n stockService: tx.stock,\n });\n const ref = { id: completion.id, bomId: run.bomId };\n const sourceType = 'ProductionRunCompletion';\n const consume = input.consume && {\n ...input.consume,\n qty,\n sourceType,\n };\n const produce = input.produce && {\n ...input.produce,\n qty,\n sourceType,\n };\n if (consume && produce) {\n ({ consumed, produced } = await inRunTenant(() =>\n production.runProduction(ref, { consume, produce }),\n ));\n } else if (consume) {\n consumed = await inRunTenant(() =>\n production.consumeMaterials(ref, consume),\n );\n } else if (produce) {\n produced = await inRunTenant(() =>\n production.produceFinishedGoods(ref, produce),\n );\n }\n }\n return { run, completion, consumed, produced };\n });\n }\n\n /** Apply `apply` to an open run and save it, in one transaction. */\n private change(\n runId: string,\n action: string,\n apply: (run: ProductionRun) => void,\n ): Promise<ProductionRun> {\n return this.inTransaction(async (tx) => {\n const run = await this.load(tx, runId);\n if (!OPEN_STATUSES.has(run.status))\n throw new ProductionRunStateError(runId, run.status, action);\n apply(run);\n await run.save();\n return run;\n });\n }\n\n private async load(\n tx: RunTransaction,\n runId: string,\n ): Promise<ProductionRun> {\n const run = UUID_PATTERN.test(runId)\n ? await tx.runs.get({ id: runId })\n : null;\n if (!run) throw new ProductionRunNotFoundError(runId);\n return run;\n }\n\n /**\n * Run `work` in one transaction on collections bound to it, rerunning the\n * whole transaction when it lost a revision race on the run row.\n */\n private inTransaction<T>(\n work: (tx: RunTransaction) => Promise<T>,\n ): Promise<T> {\n return withRevisionRetry(() =>\n this.stockService.withTransaction(async (stock) => {\n const db = stock.db;\n const [runs, completions] = await Promise.all([\n ProductionRunCollection.create({ db }),\n ProductionRunCompletionCollection.create({ db }),\n ]);\n return work({ stock, runs, completions });\n }),\n );\n }\n}\n\n/** Retries allowed for one write that keeps losing the run row's revision race. */\nconst MAX_REVISION_ATTEMPTS = 10;\n\nasync function withRevisionRetry<T>(run: () => Promise<T>): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n try {\n return await run();\n } catch (error) {\n const code = (error as { code?: string } | null)?.code;\n if (\n code !== 'RUNTIME_REVISION_CONFLICT' ||\n attempt >= MAX_REVISION_ATTEMPTS\n )\n throw error;\n }\n }\n}\n\n/**\n * `value` as a quantity: finite, rounded to the quantity precision, greater\n * than zero after rounding, and no more than {@link MAX_QUANTITY}.\n */\nfunction positive(value: number, field: string): number {\n const raw = Number(value);\n const number = Number.isFinite(raw) ? roundQuantity(raw) : raw;\n if (!Number.isFinite(number) || number <= 0)\n throw new InvalidProductionRunInputError(\n `${field} must be a number greater than zero (got ${String(value)}).`,\n );\n if (number > MAX_QUANTITY)\n throw new InvalidProductionRunInputError(\n `${field} must be at most ${MAX_QUANTITY} (got ${String(value)}).`,\n );\n return number;\n}\n\n/** Convenience factory for {@link ProductionRunService}. */\nexport function createProductionRunService(\n options: ProductionRunServiceOptions,\n): Promise<ProductionRunService> {\n return ProductionRunService.create(options);\n}\n","/**\n * RoutingService — read and replace the routing of a bill of materials.\n *\n * A routing is the ordered list of {@link RoutingStep} rows for one bill.\n * It is optional; a bill with none is valid. `replaceRouting` swaps the whole\n * list in one transaction, so reordering, inserting and removing steps are one\n * operation and a bill never shows a half-written routing.\n *\n * @packageDocumentation\n */\n\nimport { type DatabaseConfig, resolveDatabase } from '@happyvertical/smrt-core';\nimport { BillOfMaterialsCollection } from '../collections/BillOfMaterialsCollection.js';\nimport { OperationCollection } from '../collections/OperationCollection.js';\nimport { RoutingStepCollection } from '../collections/RoutingStepCollection.js';\nimport type { RoutingStep } from '../models/RoutingStep.js';\nimport {\n InvalidOperationInputError,\n OperationNotFoundError,\n OperationRetiredError,\n type RoutingStepInput,\n} from '../operation-types.js';\nimport { BomNotFoundError } from '../types.js';\n\n/** Options accepted by {@link RoutingService.create}. */\nexport interface RoutingServiceOptions {\n db: DatabaseConfig;\n}\n\ntype TransactionalDb = {\n transaction?: <T>(work: (txDb: unknown) => Promise<T>) => Promise<T>;\n};\n\nexport class RoutingService {\n private constructor(\n private readonly db: DatabaseConfig,\n public readonly boms: BillOfMaterialsCollection,\n public readonly operations: OperationCollection,\n public readonly steps: RoutingStepCollection,\n ) {}\n\n /** Factory — prefer {@link createRoutingService}. */\n static async create(options: RoutingServiceOptions): Promise<RoutingService> {\n const db = (await resolveDatabase(options.db)) as unknown as DatabaseConfig;\n const [boms, operations, steps] = await Promise.all([\n BillOfMaterialsCollection.create({ db }),\n OperationCollection.create({ db }),\n RoutingStepCollection.create({ db }),\n ]);\n return new RoutingService(db, boms, operations, steps);\n }\n\n /** The routing of a bill in step order; empty when it has none. */\n async list(bomId: string): Promise<RoutingStep[]> {\n return this.steps.findByBom(bomId);\n }\n\n /**\n * Check `inputs` against the routing about to be replaced: durations,\n * operations, and that a retired operation is not added beyond the\n * occurrences `current` already has.\n */\n private async validate(\n current: readonly RoutingStep[],\n inputs: readonly RoutingStepInput[],\n ): Promise<void> {\n // Each occurrence already on the routing may be kept once.\n const retained = new Map<string, number>();\n for (const step of current)\n retained.set(step.operationId, (retained.get(step.operationId) ?? 0) + 1);\n for (const input of inputs) {\n const minutes = Number(input.estimatedMinutes);\n if (!Number.isFinite(minutes) || minutes < 0)\n throw new InvalidOperationInputError(\n 'Estimated minutes must be zero or more.',\n );\n const operation = input.operationId\n ? await this.operations.get(input.operationId)\n : null;\n if (!operation) throw new OperationNotFoundError(input.operationId);\n const left = retained.get(operation.id as string) ?? 0;\n if (left > 0) retained.set(operation.id as string, left - 1);\n else if (!operation.isActive)\n throw new OperationRetiredError(operation.id as string);\n }\n }\n\n /**\n * Replace the bill's routing with `inputs`, numbered 1..n in the order given.\n * An empty list removes the routing.\n *\n * Throws {@link BomNotFoundError}, {@link OperationNotFoundError},\n * {@link OperationRetiredError} (a retired operation may stay on a routing\n * that already has it but cannot be newly added), or\n * {@link InvalidOperationInputError} for a negative or non-finite duration.\n */\n async replaceRouting(\n bomId: string,\n inputs: readonly RoutingStepInput[],\n ): Promise<RoutingStep[]> {\n if (!bomId || !(await this.boms.get(bomId)))\n throw new BomNotFoundError(bomId);\n\n const write = async (\n steps: RoutingStepCollection,\n boms: BillOfMaterialsCollection,\n ) => {\n // Serialize replacements of one bill: saving the bill row takes its row\n // lock for the rest of the transaction, so a concurrent replacement\n // waits instead of interleaving. The waiter's revision check then fails;\n // `withRevisionRetry` reruns it against the committed routing.\n const bom = await boms.get(bomId);\n if (!bom) throw new BomNotFoundError(bomId);\n await bom.save();\n // Validate under the lock, against the routing this attempt will replace.\n const current = await steps.findByBom(bomId);\n await this.validate(current, inputs);\n for (const step of current) await step.delete();\n const saved: RoutingStep[] = [];\n for (const [index, input] of inputs.entries()) {\n // `create()` persists; no second save.\n saved.push(\n await steps.create({\n bomId,\n operationId: input.operationId,\n sequence: index + 1,\n estimatedMinutes: Number(input.estimatedMinutes),\n notes: input.notes?.trim() ?? '',\n }),\n );\n }\n return saved;\n };\n\n const underlying = this.db as unknown as TransactionalDb;\n // Delete-and-reinsert is only safe atomically; refuse adapters without it.\n if (typeof underlying.transaction !== 'function')\n throw new Error(\n 'RoutingService requires a database adapter with transaction() support.',\n );\n return withRevisionRetry(\n () =>\n underlying.transaction?.(async (txDb) => {\n const db = txDb as DatabaseConfig;\n return write(\n await RoutingStepCollection.create({ db }),\n await BillOfMaterialsCollection.create({ db }),\n );\n }) as Promise<RoutingStep[]>,\n );\n }\n}\n\n/**\n * Rerun a whole routing transaction when it lost the bill's revision race to a\n * concurrent replacement; the rerun reads the routing the winner committed.\n */\nasync function withRevisionRetry<T>(run: () => Promise<T>): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n try {\n return await run();\n } catch (error) {\n const code = (error as { code?: string } | null)?.code;\n if (code !== 'RUNTIME_REVISION_CONFLICT' || attempt >= 5) throw error;\n }\n }\n}\n\n/** Convenience factory. */\nexport async function createRoutingService(\n options: RoutingServiceOptions,\n): Promise<RoutingService> {\n return RoutingService.create(options);\n}\n"],"mappings":"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"}