@happyvertical/smrt-manufacturing 0.53.5 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +18 -7
- package/README.md +70 -5
- package/agents/multilevel-and-runs.md +28 -0
- package/dist/collections/ProductionRunCollection.d.ts +15 -0
- package/dist/collections/ProductionRunCollection.d.ts.map +1 -0
- package/dist/collections/ProductionRunCompletionCollection.d.ts +14 -0
- package/dist/collections/ProductionRunCompletionCollection.d.ts.map +1 -0
- package/dist/collections/index.d.ts +2 -0
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/index.d.ts +13 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +972 -81
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +630 -2
- package/dist/models/ProductionRun.d.ts +67 -0
- package/dist/models/ProductionRun.d.ts.map +1 -0
- package/dist/models/ProductionRunCompletion.d.ts +36 -0
- package/dist/models/ProductionRunCompletion.d.ts.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/operation-types.d.ts +9 -0
- package/dist/operation-types.d.ts.map +1 -1
- package/dist/production-run-types.d.ts +100 -0
- package/dist/production-run-types.d.ts.map +1 -0
- package/dist/quantity.d.ts +36 -0
- package/dist/quantity.d.ts.map +1 -0
- package/dist/services/AssemblyService.d.ts +7 -2
- package/dist/services/AssemblyService.d.ts.map +1 -1
- package/dist/services/BomService.d.ts +84 -5
- package/dist/services/BomService.d.ts.map +1 -1
- package/dist/services/ProductionRunService.d.ts +123 -0
- package/dist/services/ProductionRunService.d.ts.map +1 -0
- package/dist/services/ProductionService.d.ts +11 -0
- package/dist/services/ProductionService.d.ts.map +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/structure-walk.d.ts +77 -0
- package/dist/services/structure-walk.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +514 -9
- package/dist/svelte/components/AssemblyForm.svelte +423 -52
- package/dist/svelte/components/AssemblyForm.svelte.d.ts +17 -32
- package/dist/svelte/components/AssemblyForm.svelte.d.ts.map +1 -1
- package/dist/svelte/components/AssemblyList.svelte +18 -12
- package/dist/svelte/components/AssemblyList.svelte.d.ts +2 -13
- package/dist/svelte/components/AssemblyList.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ProductionRunList.svelte +213 -0
- package/dist/svelte/components/ProductionRunList.svelte.d.ts +18 -0
- package/dist/svelte/components/ProductionRunList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/RequirementsTree.svelte +125 -0
- package/dist/svelte/components/RequirementsTree.svelte.d.ts +13 -0
- package/dist/svelte/components/RequirementsTree.svelte.d.ts.map +1 -0
- package/dist/svelte/components/RequirementsTreeLevel.svelte +120 -0
- package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts +12 -0
- package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +55 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +55 -0
- package/dist/svelte/index.d.ts +11 -6
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +11 -6
- package/dist/svelte/types.d.ts +182 -0
- package/dist/svelte/types.d.ts.map +1 -1
- package/dist/svelte/types.js +250 -0
- package/dist/tenant-scope.d.ts +25 -0
- package/dist/tenant-scope.d.ts.map +1 -0
- package/dist/types.d.ts +207 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +13 -1
- package/dist/types.js.map +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +23 -1
- package/dist/ui.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { BomNotFoundError, NoActiveBomForProductError } from "./types.js";
|
|
1
|
+
import { BomExplosionLimitError, BomNotFoundError, MAX_EXPLOSION_DEPTH, MAX_EXPLOSION_LINES, NoActiveBomForProductError } from "./types.js";
|
|
2
2
|
import { ObjectRegistry, SmrtCollection, SmrtObject, ValidationError, crossPackageRef, field, foreignKey, resolveDatabase, smrt } from "@happyvertical/smrt-core";
|
|
3
3
|
import { ProductCollection, SkuCollection } from "@happyvertical/smrt-products/collections";
|
|
4
4
|
import { Material, Product, ProductType } from "@happyvertical/smrt-products/models";
|
|
5
|
-
import { TenantScoped, getCurrentTenant, tenantId } from "@happyvertical/smrt-tenancy";
|
|
5
|
+
import { TenantScoped, getCurrentTenant, tenantId, withTenant, withTenantGlobalRead } from "@happyvertical/smrt-tenancy";
|
|
6
6
|
import { createStockService } from "@happyvertical/smrt-inventory";
|
|
7
7
|
import { createLogger } from "@happyvertical/logger";
|
|
8
8
|
//#region \0rolldown/runtime.js
|
|
9
|
-
var __defProp$
|
|
9
|
+
var __defProp$7 = Object.defineProperty;
|
|
10
10
|
var __exportAll = (all, no_symbols) => {
|
|
11
11
|
let target = {};
|
|
12
|
-
for (var name in all) __defProp$
|
|
12
|
+
for (var name in all) __defProp$7(target, name, {
|
|
13
13
|
get: all[name],
|
|
14
14
|
enumerable: true
|
|
15
15
|
});
|
|
16
|
-
if (!no_symbols) __defProp$
|
|
16
|
+
if (!no_symbols) __defProp$7(target, Symbol.toStringTag, { value: "Module" });
|
|
17
17
|
return target;
|
|
18
18
|
};
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/__smrt-register__.ts
|
|
21
|
-
ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-manufacturing\",\"packageVersion\":\"0.53.5\",\"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\"},\"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\"},\"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\"},\"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: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\"},\"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\":{\"type\":\"text\",\"required\":false},\"category\":{\"type\":\"text\",\"required\":false},\"manufacturer\":{\"type\":\"text\",\"required\":false},\"model\":{\"type\":\"text\",\"required\":false},\"price\":{\"type\":\"integer\",\"required\":true,\"default\":0},\"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\",\"exportName\":\"Assembly\",\"collectionExportName\":\"AssemblyCollection\",\"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 \\\"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},\"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_operation_id_idx\",\"columns\":[\"default_operation_id\"]}],\"version\":\"f2f2bf95\"},\"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}},\"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\",\"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: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-fields\",\"@happyvertical/smrt-inventory\",\"@happyvertical/smrt-products\",\"@happyvertical/smrt-tenancy\"]}"));
|
|
21
|
+
ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-manufacturing\",\"packageVersion\":\"0.54.0\",\"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\"},\"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\"},\"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\"},\"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\"},\"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\"},\"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\"},\"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\":{\"type\":\"text\",\"required\":false},\"category\":{\"type\":\"text\",\"required\":false},\"manufacturer\":{\"type\":\"text\",\"required\":false},\"model\":{\"type\":\"text\",\"required\":false},\"price\":{\"type\":\"integer\",\"required\":true,\"default\":0},\"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\",\"exportName\":\"Assembly\",\"collectionExportName\":\"AssemblyCollection\",\"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 \\\"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},\"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_operation_id_idx\",\"columns\":[\"default_operation_id\"]}],\"version\":\"f2f2bf95\"},\"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}},\"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\",\"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}}},\"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\"]}"));
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/models/Operation.ts
|
|
24
|
-
var __defProp$
|
|
25
|
-
var __getOwnPropDesc$
|
|
26
|
-
var __decorateClass$
|
|
27
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
24
|
+
var __defProp$6 = Object.defineProperty;
|
|
25
|
+
var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
|
|
26
|
+
var __decorateClass$6 = (decorators, target, key, kind) => {
|
|
27
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
|
|
28
28
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
29
|
-
if (kind && result) __defProp$
|
|
29
|
+
if (kind && result) __defProp$6(target, key, result);
|
|
30
30
|
return result;
|
|
31
31
|
};
|
|
32
32
|
var Operation = class extends SmrtObject {
|
|
@@ -58,11 +58,11 @@ var Operation = class extends SmrtObject {
|
|
|
58
58
|
throw new Error(`Operation ${this.code || this.id} cannot be deleted; retire it instead.`);
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
|
-
__decorateClass$
|
|
62
|
-
__decorateClass$
|
|
63
|
-
__decorateClass$
|
|
64
|
-
__decorateClass$
|
|
65
|
-
Operation = __decorateClass$
|
|
61
|
+
__decorateClass$6([tenantId({ nullable: true })], Operation.prototype, "tenantId", 2);
|
|
62
|
+
__decorateClass$6([field({ required: true })], Operation.prototype, "code", 2);
|
|
63
|
+
__decorateClass$6([field({ required: true })], Operation.prototype, "name", 2);
|
|
64
|
+
__decorateClass$6([crossPackageRef("@happyvertical/smrt-human-resources:Qualification", { nullable: true })], Operation.prototype, "requiredQualificationId", 2);
|
|
65
|
+
Operation = __decorateClass$6([TenantScoped({ mode: "optional" }), smrt({
|
|
66
66
|
tableName: "manufacturing_operations",
|
|
67
67
|
conflictColumns: ["code", "tenant_id"],
|
|
68
68
|
api: { include: ["list", "get"] },
|
|
@@ -71,12 +71,12 @@ Operation = __decorateClass$4([TenantScoped({ mode: "optional" }), smrt({
|
|
|
71
71
|
})], Operation);
|
|
72
72
|
//#endregion
|
|
73
73
|
//#region src/models/Assembly.ts
|
|
74
|
-
var __defProp$
|
|
75
|
-
var __getOwnPropDesc$
|
|
76
|
-
var __decorateClass$
|
|
77
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
74
|
+
var __defProp$5 = Object.defineProperty;
|
|
75
|
+
var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
|
|
76
|
+
var __decorateClass$5 = (decorators, target, key, kind) => {
|
|
77
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
|
|
78
78
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
79
|
-
if (kind && result) __defProp$
|
|
79
|
+
if (kind && result) __defProp$5(target, key, result);
|
|
80
80
|
return result;
|
|
81
81
|
};
|
|
82
82
|
var Assembly = class extends Product {
|
|
@@ -100,13 +100,13 @@ var Assembly = class extends Product {
|
|
|
100
100
|
if (!Number.isSafeInteger(minutes) || minutes < 0) throw ValidationError.invalidValue("estimatedLabourMinutes", minutes, "a whole number of minutes, zero or more");
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
-
__decorateClass$
|
|
104
|
-
__decorateClass$
|
|
103
|
+
__decorateClass$5([field()], Assembly.prototype, "estimatedLabourMinutes", 2);
|
|
104
|
+
__decorateClass$5([foreignKey(Operation, {
|
|
105
105
|
nullable: true,
|
|
106
106
|
onDelete: "RESTRICT"
|
|
107
107
|
})], Assembly.prototype, "defaultOperationId", 2);
|
|
108
|
-
__decorateClass$
|
|
109
|
-
Assembly = __decorateClass$
|
|
108
|
+
__decorateClass$5([field()], Assembly.prototype, "partReference", 2);
|
|
109
|
+
Assembly = __decorateClass$5([TenantScoped({ mode: "optional" }), smrt({
|
|
110
110
|
api: { include: [
|
|
111
111
|
"list",
|
|
112
112
|
"get",
|
|
@@ -123,12 +123,12 @@ var AssemblyCollection = class extends ProductCollection {
|
|
|
123
123
|
};
|
|
124
124
|
//#endregion
|
|
125
125
|
//#region src/models/BillOfMaterials.ts
|
|
126
|
-
var __defProp$
|
|
127
|
-
var __getOwnPropDesc$
|
|
128
|
-
var __decorateClass$
|
|
129
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
126
|
+
var __defProp$4 = Object.defineProperty;
|
|
127
|
+
var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
|
|
128
|
+
var __decorateClass$4 = (decorators, target, key, kind) => {
|
|
129
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
|
|
130
130
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
131
|
-
if (kind && result) __defProp$
|
|
131
|
+
if (kind && result) __defProp$4(target, key, result);
|
|
132
132
|
return result;
|
|
133
133
|
};
|
|
134
134
|
var BillOfMaterials = class extends SmrtObject {
|
|
@@ -177,11 +177,11 @@ var BillOfMaterials = class extends SmrtObject {
|
|
|
177
177
|
await (await AssemblyService.create({ db: this.db })).assertBillAcyclic(this);
|
|
178
178
|
}
|
|
179
179
|
};
|
|
180
|
-
__decorateClass$
|
|
181
|
-
__decorateClass$
|
|
182
|
-
__decorateClass$
|
|
183
|
-
__decorateClass$
|
|
184
|
-
BillOfMaterials = __decorateClass$
|
|
180
|
+
__decorateClass$4([tenantId({ nullable: true })], BillOfMaterials.prototype, "tenantId", 2);
|
|
181
|
+
__decorateClass$4([field({ required: true })], BillOfMaterials.prototype, "productId", 2);
|
|
182
|
+
__decorateClass$4([field({ required: true })], BillOfMaterials.prototype, "version", 2);
|
|
183
|
+
__decorateClass$4([field({ required: true })], BillOfMaterials.prototype, "status", 2);
|
|
184
|
+
BillOfMaterials = __decorateClass$4([TenantScoped({ mode: "optional" }), smrt({
|
|
185
185
|
tableName: "manufacturing_boms",
|
|
186
186
|
conflictColumns: [
|
|
187
187
|
"product_id",
|
|
@@ -241,12 +241,12 @@ var BillOfMaterialsCollection = class extends SmrtCollection {
|
|
|
241
241
|
};
|
|
242
242
|
//#endregion
|
|
243
243
|
//#region src/models/BomLine.ts
|
|
244
|
-
var __defProp$
|
|
245
|
-
var __getOwnPropDesc$
|
|
246
|
-
var __decorateClass$
|
|
247
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
244
|
+
var __defProp$3 = Object.defineProperty;
|
|
245
|
+
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
|
|
246
|
+
var __decorateClass$3 = (decorators, target, key, kind) => {
|
|
247
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
|
|
248
248
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
249
|
-
if (kind && result) __defProp$
|
|
249
|
+
if (kind && result) __defProp$3(target, key, result);
|
|
250
250
|
return result;
|
|
251
251
|
};
|
|
252
252
|
var BomLine = class extends SmrtObject {
|
|
@@ -299,12 +299,12 @@ var BomLine = class extends SmrtObject {
|
|
|
299
299
|
await (await AssemblyService.create({ db: this.db })).assertLineAcyclic(this);
|
|
300
300
|
}
|
|
301
301
|
};
|
|
302
|
-
__decorateClass$
|
|
303
|
-
__decorateClass$
|
|
304
|
-
__decorateClass$
|
|
305
|
-
__decorateClass$
|
|
306
|
-
__decorateClass$
|
|
307
|
-
BomLine = __decorateClass$
|
|
302
|
+
__decorateClass$3([tenantId({ nullable: true })], BomLine.prototype, "tenantId", 2);
|
|
303
|
+
__decorateClass$3([field({ required: true })], BomLine.prototype, "bomId", 2);
|
|
304
|
+
__decorateClass$3([field({ required: true })], BomLine.prototype, "componentSkuId", 2);
|
|
305
|
+
__decorateClass$3([field({ type: "decimal" })], BomLine.prototype, "qtyPerUnit", 2);
|
|
306
|
+
__decorateClass$3([field({ type: "decimal" })], BomLine.prototype, "wastePercent", 2);
|
|
307
|
+
BomLine = __decorateClass$3([TenantScoped({ mode: "optional" }), smrt({
|
|
308
308
|
tableName: "manufacturing_bom_lines",
|
|
309
309
|
conflictColumns: [
|
|
310
310
|
"bom_id",
|
|
@@ -370,6 +370,154 @@ var OperationCollection = class extends SmrtCollection {
|
|
|
370
370
|
}
|
|
371
371
|
};
|
|
372
372
|
//#endregion
|
|
373
|
+
//#region src/quantity.ts
|
|
374
|
+
var QUANTITY_DECIMALS = 6;
|
|
375
|
+
var SCALE = 10 ** 6;
|
|
376
|
+
var MAX_QUANTITY = 999999999;
|
|
377
|
+
function roundQuantity(value) {
|
|
378
|
+
return Number(value.toFixed(6));
|
|
379
|
+
}
|
|
380
|
+
function toMillionths(value) {
|
|
381
|
+
return Number(value.toFixed(6).replace(".", ""));
|
|
382
|
+
}
|
|
383
|
+
function fromMillionths(millionths) {
|
|
384
|
+
return millionths / SCALE;
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/models/ProductionRun.ts
|
|
388
|
+
var __defProp$2 = Object.defineProperty;
|
|
389
|
+
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
|
390
|
+
var __decorateClass$2 = (decorators, target, key, kind) => {
|
|
391
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
|
392
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
393
|
+
if (kind && result) __defProp$2(target, key, result);
|
|
394
|
+
return result;
|
|
395
|
+
};
|
|
396
|
+
var PRODUCTION_RUN_STATUSES = [
|
|
397
|
+
"planned",
|
|
398
|
+
"in_progress",
|
|
399
|
+
"done",
|
|
400
|
+
"cancelled"
|
|
401
|
+
];
|
|
402
|
+
var ProductionRun = class extends SmrtObject {
|
|
403
|
+
tenantId = null;
|
|
404
|
+
bomId = "";
|
|
405
|
+
targetQty = 0;
|
|
406
|
+
completedQty = 0;
|
|
407
|
+
status = "planned";
|
|
408
|
+
constructor(options = {}) {
|
|
409
|
+
super(options);
|
|
410
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
411
|
+
if (options.bomId !== void 0) this.bomId = options.bomId;
|
|
412
|
+
if (options.targetQty !== void 0) this.targetQty = options.targetQty;
|
|
413
|
+
if (options.completedQty !== void 0) this.completedQty = options.completedQty;
|
|
414
|
+
if (options.status !== void 0) this.status = options.status;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Units still to build, at the six-decimal quantity precision; `0` once
|
|
418
|
+
* the target is reached.
|
|
419
|
+
*/
|
|
420
|
+
remainingQty() {
|
|
421
|
+
return Math.max(0, roundQuantity(Number(this.targetQty) - Number(this.completedQty)));
|
|
422
|
+
}
|
|
423
|
+
/** Refuses a status outside the lifecycle and impossible quantities. */
|
|
424
|
+
async validateBeforeSave() {
|
|
425
|
+
await super.validateBeforeSave();
|
|
426
|
+
if (!PRODUCTION_RUN_STATUSES.includes(this.status)) throw new ValidationError(`Unknown production run status: ${String(this.status)}`, "MANUFACTURING_PRODUCTION_RUN_INVALID", { field: "status" });
|
|
427
|
+
const target = Number(this.targetQty);
|
|
428
|
+
const completed = Number(this.completedQty);
|
|
429
|
+
if (!Number.isFinite(target) || target <= 0) throw new ValidationError("A production run needs a target quantity greater than zero.", "MANUFACTURING_PRODUCTION_RUN_INVALID", { field: "targetQty" });
|
|
430
|
+
if (!Number.isFinite(completed) || completed < 0) throw new ValidationError("A production run cannot have a negative completed quantity.", "MANUFACTURING_PRODUCTION_RUN_INVALID", { field: "completedQty" });
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
__decorateClass$2([tenantId({ nullable: true })], ProductionRun.prototype, "tenantId", 2);
|
|
434
|
+
__decorateClass$2([foreignKey(BillOfMaterials, {
|
|
435
|
+
required: true,
|
|
436
|
+
onDelete: "RESTRICT"
|
|
437
|
+
})], ProductionRun.prototype, "bomId", 2);
|
|
438
|
+
__decorateClass$2([field({ type: "decimal" })], ProductionRun.prototype, "targetQty", 2);
|
|
439
|
+
__decorateClass$2([field({ type: "decimal" })], ProductionRun.prototype, "completedQty", 2);
|
|
440
|
+
__decorateClass$2([field({ required: true })], ProductionRun.prototype, "status", 2);
|
|
441
|
+
ProductionRun = __decorateClass$2([TenantScoped({ mode: "optional" }), smrt({
|
|
442
|
+
tableName: "manufacturing_production_runs",
|
|
443
|
+
api: { include: ["list", "get"] },
|
|
444
|
+
mcp: { include: ["list", "get"] },
|
|
445
|
+
cli: { include: ["list", "get"] }
|
|
446
|
+
})], ProductionRun);
|
|
447
|
+
//#endregion
|
|
448
|
+
//#region src/collections/ProductionRunCollection.ts
|
|
449
|
+
var ProductionRunCollection = class extends SmrtCollection {
|
|
450
|
+
static _itemClass = ProductionRun;
|
|
451
|
+
/** Runs against one bill, newest first. */
|
|
452
|
+
async findByBom(bomId) {
|
|
453
|
+
return this.list({
|
|
454
|
+
where: { bomId },
|
|
455
|
+
orderBy: "created_at DESC"
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
/** Runs in one status, oldest first (a work queue). */
|
|
459
|
+
async findByStatus(status) {
|
|
460
|
+
return this.list({
|
|
461
|
+
where: { status },
|
|
462
|
+
orderBy: "created_at ASC"
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/models/ProductionRunCompletion.ts
|
|
468
|
+
var __defProp$1 = Object.defineProperty;
|
|
469
|
+
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
470
|
+
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
471
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
472
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
473
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
474
|
+
return result;
|
|
475
|
+
};
|
|
476
|
+
var ProductionRunCompletion = class extends SmrtObject {
|
|
477
|
+
tenantId = null;
|
|
478
|
+
runId = "";
|
|
479
|
+
qty = 0;
|
|
480
|
+
/** When the units were finished (defaults to when the report was made). */
|
|
481
|
+
completedAt = /* @__PURE__ */ new Date();
|
|
482
|
+
constructor(options = {}) {
|
|
483
|
+
super(options);
|
|
484
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
485
|
+
if (options.runId !== void 0) this.runId = options.runId;
|
|
486
|
+
if (options.qty !== void 0) this.qty = options.qty;
|
|
487
|
+
if (options.completedAt !== void 0) this.completedAt = options.completedAt instanceof Date ? options.completedAt : new Date(options.completedAt);
|
|
488
|
+
}
|
|
489
|
+
/** Refuses a report of zero, negative or non-finite units. */
|
|
490
|
+
async validateBeforeSave() {
|
|
491
|
+
await super.validateBeforeSave();
|
|
492
|
+
const qty = Number(this.qty);
|
|
493
|
+
if (!Number.isFinite(qty) || qty <= 0) throw new ValidationError("A completion needs a quantity greater than zero.", "MANUFACTURING_PRODUCTION_RUN_INVALID", { field: "qty" });
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
__decorateClass$1([tenantId({ nullable: true })], ProductionRunCompletion.prototype, "tenantId", 2);
|
|
497
|
+
__decorateClass$1([foreignKey(ProductionRun, {
|
|
498
|
+
required: true,
|
|
499
|
+
onDelete: "CASCADE"
|
|
500
|
+
})], ProductionRunCompletion.prototype, "runId", 2);
|
|
501
|
+
__decorateClass$1([field({ type: "decimal" })], ProductionRunCompletion.prototype, "qty", 2);
|
|
502
|
+
ProductionRunCompletion = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt({
|
|
503
|
+
tableName: "manufacturing_production_run_completions",
|
|
504
|
+
api: { include: ["list", "get"] },
|
|
505
|
+
mcp: { include: ["list", "get"] },
|
|
506
|
+
cli: { include: ["list", "get"] }
|
|
507
|
+
})], ProductionRunCompletion);
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region src/collections/ProductionRunCompletionCollection.ts
|
|
510
|
+
var ProductionRunCompletionCollection = class extends SmrtCollection {
|
|
511
|
+
static _itemClass = ProductionRunCompletion;
|
|
512
|
+
/** A run's completions in the order they happened. */
|
|
513
|
+
async findByRun(runId) {
|
|
514
|
+
return this.list({
|
|
515
|
+
where: { runId },
|
|
516
|
+
orderBy: ["completedAt ASC", "created_at ASC"]
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
//#endregion
|
|
373
521
|
//#region src/models/RoutingStep.ts
|
|
374
522
|
var __defProp = Object.defineProperty;
|
|
375
523
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -468,6 +616,53 @@ var InvalidOperationInputError = class extends Error {
|
|
|
468
616
|
name = "InvalidOperationInputError";
|
|
469
617
|
};
|
|
470
618
|
//#endregion
|
|
619
|
+
//#region src/production-run-types.ts
|
|
620
|
+
var ProductionRunNotFoundError = class extends Error {
|
|
621
|
+
constructor(runId) {
|
|
622
|
+
super(`Production run not found: ${runId}`);
|
|
623
|
+
this.runId = runId;
|
|
624
|
+
}
|
|
625
|
+
runId;
|
|
626
|
+
name = "ProductionRunNotFoundError";
|
|
627
|
+
};
|
|
628
|
+
var ProductionRunStateError = class extends Error {
|
|
629
|
+
constructor(runId, status, action) {
|
|
630
|
+
super(`Production run ${runId} is ${status}; cannot ${action} it.`);
|
|
631
|
+
this.runId = runId;
|
|
632
|
+
this.status = status;
|
|
633
|
+
this.action = action;
|
|
634
|
+
}
|
|
635
|
+
runId;
|
|
636
|
+
status;
|
|
637
|
+
action;
|
|
638
|
+
name = "ProductionRunStateError";
|
|
639
|
+
};
|
|
640
|
+
var ProductionRunOverCompletionError = class extends Error {
|
|
641
|
+
constructor(runId, qty, remaining) {
|
|
642
|
+
super(`Production run ${runId} has ${remaining} left to build; cannot report ${qty}.`);
|
|
643
|
+
this.runId = runId;
|
|
644
|
+
this.qty = qty;
|
|
645
|
+
this.remaining = remaining;
|
|
646
|
+
}
|
|
647
|
+
runId;
|
|
648
|
+
qty;
|
|
649
|
+
remaining;
|
|
650
|
+
name = "ProductionRunOverCompletionError";
|
|
651
|
+
};
|
|
652
|
+
var InvalidProductionRunInputError = class extends Error {
|
|
653
|
+
name = "InvalidProductionRunInputError";
|
|
654
|
+
};
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region src/tenant-scope.ts
|
|
657
|
+
function readOwnAndGlobal(owner, read) {
|
|
658
|
+
const scope = owner ?? getCurrentTenant()?.tenantId ?? null;
|
|
659
|
+
return scope ? withTenantGlobalRead(scope, read) : read();
|
|
660
|
+
}
|
|
661
|
+
function isOwnOrGlobal(row, owner) {
|
|
662
|
+
const rowTenant = row.tenantId ?? null;
|
|
663
|
+
return rowTenant === null || rowTenant === owner;
|
|
664
|
+
}
|
|
665
|
+
//#endregion
|
|
471
666
|
//#region src/services/AssemblyService.ts
|
|
472
667
|
var AssemblyService_exports = /* @__PURE__ */ __exportAll({
|
|
473
668
|
AssemblyService: () => AssemblyService,
|
|
@@ -488,14 +683,14 @@ var BomCycleError = class extends ValidationError {
|
|
|
488
683
|
path;
|
|
489
684
|
name = "BomCycleError";
|
|
490
685
|
};
|
|
491
|
-
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
686
|
+
var UUID_PATTERN$2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
492
687
|
function visibleTo(row, options) {
|
|
493
688
|
if (options.tenantId === void 0) return true;
|
|
494
689
|
const rowTenant = row.tenantId ?? null;
|
|
495
690
|
return rowTenant === null || rowTenant === options.tenantId;
|
|
496
691
|
}
|
|
497
692
|
function isUuid(value) {
|
|
498
|
-
return typeof value === "string" && UUID_PATTERN.test(value);
|
|
693
|
+
return typeof value === "string" && UUID_PATTERN$2.test(value);
|
|
499
694
|
}
|
|
500
695
|
var AssemblyService = class AssemblyService {
|
|
501
696
|
constructor(products, skus, boms, lines) {
|
|
@@ -540,10 +735,10 @@ var AssemblyService = class AssemblyService {
|
|
|
540
735
|
buildable: false
|
|
541
736
|
});
|
|
542
737
|
if (!isUuid(skuId)) return missing(null);
|
|
543
|
-
const sku = await this.skus
|
|
738
|
+
const sku = await this.byId(this.skus, skuId, options);
|
|
544
739
|
if (!sku || !visibleTo(sku, options)) return missing(null);
|
|
545
740
|
if (!isUuid(sku.productId)) return missing(sku);
|
|
546
|
-
const product = await this.products
|
|
741
|
+
const product = await this.byId(this.products, sku.productId, options);
|
|
547
742
|
if (!product || !visibleTo(product, options)) return missing(sku);
|
|
548
743
|
if (product instanceof Assembly) {
|
|
549
744
|
const activeBom = await this.findActiveBom(product.id, options);
|
|
@@ -568,18 +763,32 @@ var AssemblyService = class AssemblyService {
|
|
|
568
763
|
};
|
|
569
764
|
}
|
|
570
765
|
/**
|
|
571
|
-
* The highest-version active bill for a product
|
|
572
|
-
*
|
|
766
|
+
* The highest-version active bill for a product. With `options.tenantId`,
|
|
767
|
+
* that tenant's own bill, or else a global one; another tenant's never.
|
|
573
768
|
*/
|
|
574
769
|
async findActiveBom(productId, options = {}) {
|
|
575
|
-
|
|
576
|
-
|
|
770
|
+
const owner = options.tenantId;
|
|
771
|
+
if (owner === void 0) return this.boms.findActiveForProduct(productId);
|
|
772
|
+
const active = await readOwnAndGlobal(owner, () => this.boms.list({
|
|
577
773
|
where: {
|
|
578
774
|
productId,
|
|
579
775
|
status: "active"
|
|
580
776
|
},
|
|
581
777
|
orderBy: "version DESC"
|
|
582
|
-
}))
|
|
778
|
+
}));
|
|
779
|
+
return active.find((bom) => (bom.tenantId ?? null) === owner) ?? active.find((bom) => (bom.tenantId ?? null) === null) ?? null;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* One row by id: through the collection's `get` without an explicit
|
|
783
|
+
* tenant, else as a list read of that tenant's and global rows.
|
|
784
|
+
*/
|
|
785
|
+
async byId(collection, id, options) {
|
|
786
|
+
const owner = options.tenantId;
|
|
787
|
+
if (owner === void 0) return collection.get({ id });
|
|
788
|
+
return (await readOwnAndGlobal(owner, () => collection.list({
|
|
789
|
+
where: { id },
|
|
790
|
+
limit: 1
|
|
791
|
+
})))[0] ?? null;
|
|
583
792
|
}
|
|
584
793
|
/** `true` when the SKU belongs to an {@link Assembly}. */
|
|
585
794
|
async isAssembly(skuId) {
|
|
@@ -597,7 +806,7 @@ var AssemblyService = class AssemblyService {
|
|
|
597
806
|
if (!bom) throw new BomNotFoundError(bomId);
|
|
598
807
|
const cache = /* @__PURE__ */ new Map();
|
|
599
808
|
const scope = { tenantId: bom.tenantId ?? null };
|
|
600
|
-
const lines = (await this.lines.findByBom(bomId)).filter((line) => visibleTo(line, scope));
|
|
809
|
+
const lines = (await readOwnAndGlobal(scope.tenantId, () => this.lines.findByBom(bomId))).filter((line) => visibleTo(line, scope));
|
|
601
810
|
const resolved = [];
|
|
602
811
|
for (const line of lines) resolved.push({
|
|
603
812
|
line,
|
|
@@ -626,7 +835,8 @@ var AssemblyService = class AssemblyService {
|
|
|
626
835
|
if (product.id === productId) return [...trail2, product];
|
|
627
836
|
if (!component.activeBom || visited.has(product.id)) continue;
|
|
628
837
|
visited.add(product.id);
|
|
629
|
-
const
|
|
838
|
+
const billId = component.activeBom.id;
|
|
839
|
+
const lines = (options.tenantId === void 0 ? await this.lines.findByBom(billId) : await readOwnAndGlobal(options.tenantId, () => this.lines.findByBom(billId))).filter((line) => visibleTo(line, options));
|
|
630
840
|
const found = await walk(lines.map((line) => line.componentSkuId), [...trail2, product]);
|
|
631
841
|
if (found) return found;
|
|
632
842
|
}
|
|
@@ -672,13 +882,13 @@ var AssemblyService = class AssemblyService {
|
|
|
672
882
|
const billIds = /* @__PURE__ */ new Set();
|
|
673
883
|
if (isUuid(bom.id)) billIds.add(bom.id);
|
|
674
884
|
const tenantId = bom.tenantId ?? getCurrentTenant()?.tenantId ?? null;
|
|
675
|
-
const sameKey = await this.boms.list({ where: {
|
|
885
|
+
const sameKey = await readOwnAndGlobal(tenantId, () => this.boms.list({ where: {
|
|
676
886
|
productId: bom.productId,
|
|
677
887
|
version: bom.version
|
|
678
|
-
} });
|
|
888
|
+
} }));
|
|
679
889
|
for (const stored of sameKey) if (stored.id && (stored.tenantId ?? null) === tenantId) billIds.add(stored.id);
|
|
680
890
|
const componentSkuIds = [];
|
|
681
|
-
for (const billId of billIds) for (const line of await this.lines.findByBom(billId)) if (visibleTo(line, { tenantId })) componentSkuIds.push(line.componentSkuId);
|
|
891
|
+
for (const billId of billIds) for (const line of await readOwnAndGlobal(tenantId, () => this.lines.findByBom(billId))) if (visibleTo(line, { tenantId })) componentSkuIds.push(line.componentSkuId);
|
|
682
892
|
if (componentSkuIds.length === 0) return;
|
|
683
893
|
const path = await this.findCycle(bom.productId, componentSkuIds, { tenantId });
|
|
684
894
|
if (path) throw new BomCycleError(bom.id || [...billIds][0] || "", path);
|
|
@@ -696,9 +906,123 @@ function createAssemblyService(options) {
|
|
|
696
906
|
return AssemblyService.create(options);
|
|
697
907
|
}
|
|
698
908
|
//#endregion
|
|
909
|
+
//#region src/services/structure-walk.ts
|
|
910
|
+
var BomStructureCycleError = class extends BomCycleError {
|
|
911
|
+
name = "BomStructureCycleError";
|
|
912
|
+
constructor(bomId, path) {
|
|
913
|
+
super(bomId, path);
|
|
914
|
+
this.message = `The bill structure contains a cycle: ${path.map((entry) => entry.name).join(" → ")}`;
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
var UUID_PATTERN$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
918
|
+
function maxLevelFor(levels) {
|
|
919
|
+
if (levels === void 0) return 1;
|
|
920
|
+
if (levels === "all") return 32;
|
|
921
|
+
if (!Number.isInteger(levels) || levels < 1 || levels > 32) throw new Error(`levels must be a whole number from 1 to 32, or 'all' (got ${String(levels)})`);
|
|
922
|
+
return levels;
|
|
923
|
+
}
|
|
924
|
+
function isMultiLevel(levels) {
|
|
925
|
+
return maxLevelFor(levels) > 1;
|
|
926
|
+
}
|
|
927
|
+
var StructureWalk = class {
|
|
928
|
+
constructor(assemblies, top, levels) {
|
|
929
|
+
this.assemblies = assemblies;
|
|
930
|
+
this.top = top;
|
|
931
|
+
this.levels = levels;
|
|
932
|
+
this.maxLevel = maxLevelFor(levels);
|
|
933
|
+
}
|
|
934
|
+
assemblies;
|
|
935
|
+
top;
|
|
936
|
+
levels;
|
|
937
|
+
components = /* @__PURE__ */ new Map();
|
|
938
|
+
bills = /* @__PURE__ */ new Map();
|
|
939
|
+
lineCount = 0;
|
|
940
|
+
/** The deepest level whose lines the walk reads. */
|
|
941
|
+
maxLevel;
|
|
942
|
+
/**
|
|
943
|
+
* Resolve a component SKU, reading assemblies' active bills in the top
|
|
944
|
+
* bill's tenant so a shared assembly opens that tenant's bill.
|
|
945
|
+
*/
|
|
946
|
+
resolve(skuId) {
|
|
947
|
+
let pending = this.components.get(skuId);
|
|
948
|
+
if (!pending) {
|
|
949
|
+
pending = this.assemblies.resolveComponent(skuId, { tenantId: this.top.tenantId ?? null });
|
|
950
|
+
this.components.set(skuId, pending);
|
|
951
|
+
}
|
|
952
|
+
return pending;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* A bill's lines in the top bill's tenant (and global lines), counted
|
|
956
|
+
* against {@link MAX_EXPLOSION_LINES} every time the walk visits them (a
|
|
957
|
+
* shared sub-assembly counts once per use).
|
|
958
|
+
*/
|
|
959
|
+
async linesOf(bomId) {
|
|
960
|
+
let pending = this.bills.get(bomId);
|
|
961
|
+
if (!pending) {
|
|
962
|
+
const tenantId = this.top.tenantId ?? null;
|
|
963
|
+
pending = readOwnAndGlobal(tenantId, () => this.assemblies.lines.findByBom(bomId)).then((lines2) => lines2.filter((line) => isOwnOrGlobal(line, tenantId)));
|
|
964
|
+
this.bills.set(bomId, pending);
|
|
965
|
+
}
|
|
966
|
+
const lines = await pending;
|
|
967
|
+
this.lineCount += lines.length;
|
|
968
|
+
if (this.lineCount > 1e4) throw new BomExplosionLimitError(this.top.id, "lines");
|
|
969
|
+
return lines;
|
|
970
|
+
}
|
|
971
|
+
/** The path entry for the top bill. */
|
|
972
|
+
async root() {
|
|
973
|
+
const productId = this.top.productId;
|
|
974
|
+
let name = productId;
|
|
975
|
+
if (UUID_PATTERN$1.test(productId)) {
|
|
976
|
+
const owner = this.top.tenantId ?? null;
|
|
977
|
+
const [product] = await readOwnAndGlobal(owner, () => this.assemblies.products.list({
|
|
978
|
+
where: { id: productId },
|
|
979
|
+
limit: 1
|
|
980
|
+
}));
|
|
981
|
+
if (product?.name && isOwnOrGlobal(product, owner)) name = product.name;
|
|
982
|
+
}
|
|
983
|
+
return {
|
|
984
|
+
bomId: this.top.id,
|
|
985
|
+
productId,
|
|
986
|
+
name
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* The bill to open below a line at `level`, or `null` when the component
|
|
991
|
+
* is not a sub-assembly with an active bill or `levels` stops here.
|
|
992
|
+
*
|
|
993
|
+
* @throws {BomStructureCycleError} when the sub-assembly is already on
|
|
994
|
+
* `path` (it would contain itself).
|
|
995
|
+
* @throws {BomExplosionLimitError} when `levels: 'all'` would go deeper
|
|
996
|
+
* than {@link MAX_EXPLOSION_DEPTH}.
|
|
997
|
+
*/
|
|
998
|
+
open(component, level, path) {
|
|
999
|
+
const bom = component.activeBom;
|
|
1000
|
+
const product = component.product;
|
|
1001
|
+
if (!component.buildable || !bom?.id || !product?.id) return null;
|
|
1002
|
+
const name = product.name || product.id;
|
|
1003
|
+
const at = path.findIndex((entry) => entry.productId === product.id);
|
|
1004
|
+
if (at >= 0) throw new BomStructureCycleError(this.top.id, [...path.slice(at).map((entry) => ({
|
|
1005
|
+
productId: entry.productId,
|
|
1006
|
+
name: entry.name
|
|
1007
|
+
})), {
|
|
1008
|
+
productId: product.id,
|
|
1009
|
+
name
|
|
1010
|
+
}]);
|
|
1011
|
+
if (level >= this.maxLevel) {
|
|
1012
|
+
if (this.levels === "all") throw new BomExplosionLimitError(this.top.id, "depth");
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
return {
|
|
1016
|
+
bomId: bom.id,
|
|
1017
|
+
productId: product.id,
|
|
1018
|
+
name
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
//#endregion
|
|
699
1023
|
//#region src/services/BomService.ts
|
|
700
1024
|
var BomService = class BomService {
|
|
701
|
-
constructor(boms, lines, stockService, costResolver, routingSteps, operations, rateResolver
|
|
1025
|
+
constructor(boms, lines, stockService, costResolver, routingSteps, operations, rateResolver, assemblies) {
|
|
702
1026
|
this.boms = boms;
|
|
703
1027
|
this.lines = lines;
|
|
704
1028
|
this.stockService = stockService;
|
|
@@ -706,6 +1030,7 @@ var BomService = class BomService {
|
|
|
706
1030
|
this.routingSteps = routingSteps;
|
|
707
1031
|
this.operations = operations;
|
|
708
1032
|
this.rateResolver = rateResolver;
|
|
1033
|
+
this.assemblies = assemblies;
|
|
709
1034
|
}
|
|
710
1035
|
boms;
|
|
711
1036
|
lines;
|
|
@@ -714,17 +1039,19 @@ var BomService = class BomService {
|
|
|
714
1039
|
routingSteps;
|
|
715
1040
|
operations;
|
|
716
1041
|
rateResolver;
|
|
1042
|
+
assemblies;
|
|
717
1043
|
/** Factory — prefer {@link createBomService}. */
|
|
718
1044
|
static async create(options) {
|
|
719
1045
|
const stockService = options.stockService ?? await buildStockService$1(options);
|
|
720
1046
|
const sharedDb = await resolveDatabase(options.stockService ? options.db ?? stockService.db : stockService.db);
|
|
721
|
-
const [boms, lines, routingSteps, operations] = await Promise.all([
|
|
1047
|
+
const [boms, lines, routingSteps, operations, assemblies] = await Promise.all([
|
|
722
1048
|
BillOfMaterialsCollection.create({ db: sharedDb }),
|
|
723
1049
|
BomLineCollection.create({ db: sharedDb }),
|
|
724
1050
|
RoutingStepCollection.create({ db: sharedDb }),
|
|
725
|
-
OperationCollection.create({ db: sharedDb })
|
|
1051
|
+
OperationCollection.create({ db: sharedDb }),
|
|
1052
|
+
AssemblyService.create({ db: sharedDb })
|
|
726
1053
|
]);
|
|
727
|
-
return new BomService(boms, lines, stockService, options.costResolver, routingSteps, operations, options.rateResolver);
|
|
1054
|
+
return new BomService(boms, lines, stockService, options.costResolver, routingSteps, operations, options.rateResolver, assemblies);
|
|
728
1055
|
}
|
|
729
1056
|
/**
|
|
730
1057
|
* Walk every {@link BomLine} for the given BOM, resolve each line's
|
|
@@ -737,10 +1064,19 @@ var BomService = class BomService {
|
|
|
737
1064
|
* set `costUnavailable: true` on their breakdown row; the aggregate
|
|
738
1065
|
* `hasMissingCosts` flag mirrors this so callers can surface a UI
|
|
739
1066
|
* warning.
|
|
1067
|
+
*
|
|
1068
|
+
* With `{ levels }` above 1, a sub-assembly line whose bill the walk opens
|
|
1069
|
+
* is costed from that bill instead of the resolver: its `unitCost` is the
|
|
1070
|
+
* bill's rolled-up material cost per unit, its `components` hold that
|
|
1071
|
+
* bill's lines, and a missing cost anywhere below marks it and
|
|
1072
|
+
* `hasMissingCosts` (its known part still counts, so the total is a lower
|
|
1073
|
+
* bound). Other lines, including sub-assemblies with no active bill, use
|
|
1074
|
+
* the resolver as before.
|
|
740
1075
|
*/
|
|
741
|
-
async computeMaterialCost(bomId) {
|
|
1076
|
+
async computeMaterialCost(bomId, options = {}) {
|
|
1077
|
+
if (isMultiLevel(options.levels)) return this.computeMultiLevelCost(bomId, options);
|
|
742
1078
|
const bom = await this.requireBom(bomId);
|
|
743
|
-
const lines = await this.
|
|
1079
|
+
const lines = await this.linesOf(bom);
|
|
744
1080
|
const lineBreakdown = [];
|
|
745
1081
|
let totalCost = 0;
|
|
746
1082
|
let hasMissingCosts = false;
|
|
@@ -778,17 +1114,30 @@ var BomService = class BomService {
|
|
|
778
1114
|
* estimate with zero totals, so callers can add it to the material cost
|
|
779
1115
|
* unconditionally. Retired operations still count: the routing names them.
|
|
780
1116
|
*
|
|
1117
|
+
* With `{ levels }` above 1, every sub-assembly the walk opens adds its own
|
|
1118
|
+
* bill's routing, multiplied by the units of it one unit needs (waste
|
|
1119
|
+
* included); see {@link LabourEstimate.subAssemblies}.
|
|
1120
|
+
*
|
|
781
1121
|
* Throws {@link BomNotFoundError} when the BOM does not exist.
|
|
782
1122
|
*/
|
|
783
|
-
async computeLabourEstimate(bomId) {
|
|
784
|
-
|
|
785
|
-
|
|
1123
|
+
async computeLabourEstimate(bomId, options = {}) {
|
|
1124
|
+
if (isMultiLevel(options.levels)) return this.computeMultiLevelLabour(bomId, options);
|
|
1125
|
+
return this.labourFor(await this.requireBom(bomId));
|
|
1126
|
+
}
|
|
1127
|
+
/** One bill's own routing estimate, per unit of its product. */
|
|
1128
|
+
async labourFor(bom) {
|
|
1129
|
+
const bomId = bom.id;
|
|
1130
|
+
const owner = bom.tenantId ?? null;
|
|
1131
|
+
const routing = (await readOwnAndGlobal(owner, () => this.routingSteps.findByBom(bomId))).filter((step) => isOwnOrGlobal(step, owner));
|
|
786
1132
|
const steps = [];
|
|
787
1133
|
let totalMinutes = 0;
|
|
788
1134
|
let totalCost = 0;
|
|
789
1135
|
let hasMissingRates = false;
|
|
790
1136
|
for (const step of routing) {
|
|
791
|
-
const operation = await this.operations.
|
|
1137
|
+
const [operation] = (await readOwnAndGlobal(owner, () => this.operations.list({
|
|
1138
|
+
where: { id: step.operationId },
|
|
1139
|
+
limit: 1
|
|
1140
|
+
}))).filter((row) => isOwnOrGlobal(row, owner));
|
|
792
1141
|
const minutes = Number(step.estimatedMinutes ?? 0);
|
|
793
1142
|
const rate = operation ? await this.resolveRate(operation) : null;
|
|
794
1143
|
const rateUnavailable = rate === null;
|
|
@@ -828,13 +1177,17 @@ var BomService = class BomService {
|
|
|
828
1177
|
* {@link ProductionService.consumeMaterials} when you're ready to write
|
|
829
1178
|
* stock movements.
|
|
830
1179
|
*
|
|
1180
|
+
* With `{ levels }` above 1, sub-assemblies with an active bill are
|
|
1181
|
+
* replaced by what their bills need, waste compounding down the levels;
|
|
1182
|
+
* this is {@link explode}'s `totals`.
|
|
1183
|
+
*
|
|
831
1184
|
* Throws {@link BomNotFoundError} when the BOM does not exist; throws a
|
|
832
1185
|
* plain `Error` when `qty` is not a positive finite number.
|
|
833
1186
|
*/
|
|
834
|
-
async explodeRequirements(bomId, qty) {
|
|
1187
|
+
async explodeRequirements(bomId, qty, options = {}) {
|
|
1188
|
+
if (isMultiLevel(options.levels)) return (await this.explode(bomId, qty, options)).totals;
|
|
835
1189
|
assertPositiveQty$1(qty, "explodeRequirements");
|
|
836
|
-
await this.requireBom(bomId);
|
|
837
|
-
const lines = await this.lines.findByBom(bomId);
|
|
1190
|
+
const lines = await this.linesOf(await this.requireBom(bomId));
|
|
838
1191
|
const aggregate = /* @__PURE__ */ new Map();
|
|
839
1192
|
for (const line of lines) {
|
|
840
1193
|
const effectiveQty = line.effectiveQtyPerUnit() * qty;
|
|
@@ -860,9 +1213,25 @@ var BomService = class BomService {
|
|
|
860
1213
|
* "where do we pull from?" is left to the caller of
|
|
861
1214
|
* {@link ProductionService.consumeMaterials}).
|
|
862
1215
|
*
|
|
1216
|
+
* With `{ levels }` above 1 the check is {@link planRequirements}: a
|
|
1217
|
+
* sub-assembly that is short and has an active bill is opened for its
|
|
1218
|
+
* shortfall, and each shortage is reported on the line where it occurs,
|
|
1219
|
+
* with its `level` and `path`. Stock is allocated as the walk goes, so a
|
|
1220
|
+
* component used in two places is not counted twice.
|
|
1221
|
+
*
|
|
863
1222
|
* Throws {@link BomNotFoundError} when the BOM does not exist.
|
|
864
1223
|
*/
|
|
865
|
-
async canProduce(bomId, qty) {
|
|
1224
|
+
async canProduce(bomId, qty, options = {}) {
|
|
1225
|
+
if (isMultiLevel(options.levels)) {
|
|
1226
|
+
const plan = await this.planRequirements(bomId, qty, options);
|
|
1227
|
+
return plan.ok ? {
|
|
1228
|
+
ok: true,
|
|
1229
|
+
shortages: []
|
|
1230
|
+
} : {
|
|
1231
|
+
ok: false,
|
|
1232
|
+
shortages: plan.shortages
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
866
1235
|
const requirements = await this.explodeRequirements(bomId, qty);
|
|
867
1236
|
const availabilities = await Promise.all(requirements.map(async (requirement) => ({
|
|
868
1237
|
requirement,
|
|
@@ -884,15 +1253,254 @@ var BomService = class BomService {
|
|
|
884
1253
|
};
|
|
885
1254
|
}
|
|
886
1255
|
/**
|
|
1256
|
+
* Walk the bill for `qty` units and return every line, depth first, with
|
|
1257
|
+
* its level, the path of bills above it and its quantity for the run, plus
|
|
1258
|
+
* the rolled-up `totals`. Gross requirements: stock is not read.
|
|
1259
|
+
*
|
|
1260
|
+
* `levels` (default `1`) says how far to open sub-assemblies; see
|
|
1261
|
+
* {@link ExplosionLevels}. Waste compounds: a sub-assembly line's quantity
|
|
1262
|
+
* includes its own waste, and its bill's lines are multiplied by it.
|
|
1263
|
+
*
|
|
1264
|
+
* Throws {@link BomNotFoundError}, `BomStructureCycleError` when stored
|
|
1265
|
+
* data already has a product containing itself, `BomExplosionLimitError`
|
|
1266
|
+
* past the depth or line limits, and a plain `Error` for a bad `qty` or
|
|
1267
|
+
* `levels`.
|
|
1268
|
+
*/
|
|
1269
|
+
async explode(bomId, qty, options = {}) {
|
|
1270
|
+
assertPositiveQty$1(qty, "explode");
|
|
1271
|
+
const levels = options.levels ?? 1;
|
|
1272
|
+
const top = await this.requireBom(bomId);
|
|
1273
|
+
const walk = new StructureWalk(this.assemblies, top, levels);
|
|
1274
|
+
const lines = [];
|
|
1275
|
+
const visit = async (billId, level, path, parentQty) => {
|
|
1276
|
+
for (const line of await walk.linesOf(billId)) {
|
|
1277
|
+
const component = await walk.resolve(line.componentSkuId);
|
|
1278
|
+
const sub = walk.open(component, level, path);
|
|
1279
|
+
const exploded = explodedLine(line, component, level, path, parentQty);
|
|
1280
|
+
exploded.expanded = sub !== null;
|
|
1281
|
+
lines.push(exploded);
|
|
1282
|
+
if (sub) await visit(sub.bomId, level + 1, [...path, sub], exploded.totalQty);
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
await visit(bomId, 1, [await walk.root()], qty);
|
|
1286
|
+
return {
|
|
1287
|
+
bomId,
|
|
1288
|
+
productId: top.productId,
|
|
1289
|
+
qty,
|
|
1290
|
+
levels,
|
|
1291
|
+
lines,
|
|
1292
|
+
totals: sumRequirements(lines.filter((line) => !line.expanded))
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Net the walk against stock: for each line, the available stock (all
|
|
1297
|
+
* locations) not yet allocated to an earlier line and how much is short.
|
|
1298
|
+
* A sub-assembly that is short and has an active bill is opened for the
|
|
1299
|
+
* shortfall only, so its lines say what building that shortfall takes;
|
|
1300
|
+
* one covered by stock is not opened. Lines that are short and cannot be
|
|
1301
|
+
* opened are the `shortages`, each at its own level.
|
|
1302
|
+
*
|
|
1303
|
+
* These are facts, not a decision: the application decides whether a
|
|
1304
|
+
* short sub-assembly is built or bought. Does not mutate stock.
|
|
1305
|
+
*
|
|
1306
|
+
* Throws as {@link explode} does.
|
|
1307
|
+
*/
|
|
1308
|
+
async planRequirements(bomId, qty, options = {}) {
|
|
1309
|
+
assertPositiveQty$1(qty, "planRequirements");
|
|
1310
|
+
const levels = options.levels ?? 1;
|
|
1311
|
+
const top = await this.requireBom(bomId);
|
|
1312
|
+
const walk = new StructureWalk(this.assemblies, top, levels);
|
|
1313
|
+
const lines = [];
|
|
1314
|
+
const shortages = [];
|
|
1315
|
+
const unallocated = /* @__PURE__ */ new Map();
|
|
1316
|
+
const tenantId = top.tenantId ?? null;
|
|
1317
|
+
const availableFor = (skuId) => {
|
|
1318
|
+
let pending = unallocated.get(skuId);
|
|
1319
|
+
if (!pending) {
|
|
1320
|
+
pending = this.availableInTenant(skuId, tenantId);
|
|
1321
|
+
unallocated.set(skuId, pending);
|
|
1322
|
+
}
|
|
1323
|
+
return pending;
|
|
1324
|
+
};
|
|
1325
|
+
const visit = async (billId, level, path, parentQty) => {
|
|
1326
|
+
for (const line of await walk.linesOf(billId)) {
|
|
1327
|
+
const component = await walk.resolve(line.componentSkuId);
|
|
1328
|
+
const base = explodedLine(line, component, level, path, parentQty);
|
|
1329
|
+
const available = await availableFor(line.componentSkuId);
|
|
1330
|
+
const allocated = Math.min(available, base.totalQty);
|
|
1331
|
+
unallocated.set(line.componentSkuId, Promise.resolve(available - allocated));
|
|
1332
|
+
const short = Math.max(0, roundQuantity(base.totalQty - allocated));
|
|
1333
|
+
const sub = short > 0 ? walk.open(component, level, path) : null;
|
|
1334
|
+
lines.push({
|
|
1335
|
+
...base,
|
|
1336
|
+
expanded: sub !== null,
|
|
1337
|
+
available,
|
|
1338
|
+
short
|
|
1339
|
+
});
|
|
1340
|
+
if (sub) await visit(sub.bomId, level + 1, [...path, sub], short);
|
|
1341
|
+
else if (short > 0) shortages.push({
|
|
1342
|
+
componentSkuId: line.componentSkuId,
|
|
1343
|
+
requested: base.totalQty,
|
|
1344
|
+
available,
|
|
1345
|
+
level,
|
|
1346
|
+
path,
|
|
1347
|
+
uom: base.uom,
|
|
1348
|
+
buildable: component.buildable
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
await visit(bomId, 1, [await walk.root()], qty);
|
|
1353
|
+
return {
|
|
1354
|
+
bomId,
|
|
1355
|
+
productId: top.productId,
|
|
1356
|
+
qty,
|
|
1357
|
+
levels,
|
|
1358
|
+
lines,
|
|
1359
|
+
shortages,
|
|
1360
|
+
ok: shortages.length === 0
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
887
1364
|
* Fetch a BOM by id or throw {@link BomNotFoundError}. Centralises the
|
|
888
1365
|
* "missing BOM" error path so callers don't have to repeat the check.
|
|
889
1366
|
*/
|
|
1367
|
+
/**
|
|
1368
|
+
* A bill's lines: its own tenant's and global ones, whatever tenant
|
|
1369
|
+
* context is active (a context alone would hide a global bill's lines).
|
|
1370
|
+
*/
|
|
1371
|
+
async linesOf(bom) {
|
|
1372
|
+
const owner = bom.tenantId ?? null;
|
|
1373
|
+
return (await readOwnAndGlobal(owner, () => this.lines.findByBom(bom.id))).filter((line) => isOwnOrGlobal(line, owner));
|
|
1374
|
+
}
|
|
890
1375
|
async requireBom(bomId) {
|
|
891
1376
|
if (!bomId) throw new BomNotFoundError(bomId);
|
|
892
|
-
const
|
|
1377
|
+
const tenantId = getCurrentTenant()?.tenantId;
|
|
1378
|
+
const bom = tenantId ? (await readOwnAndGlobal(tenantId, () => this.boms.list({
|
|
1379
|
+
where: { id: bomId },
|
|
1380
|
+
limit: 1
|
|
1381
|
+
}))).find((row) => isOwnOrGlobal(row, tenantId)) ?? null : await this.boms.get(bomId);
|
|
893
1382
|
if (!bom) throw new BomNotFoundError(bomId);
|
|
894
1383
|
return bom;
|
|
895
1384
|
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Available stock of a SKU across locations, counting only the top bill's
|
|
1387
|
+
* tenant's rows and global ones, whatever tenant context is active (the
|
|
1388
|
+
* walk reads its structure the same way).
|
|
1389
|
+
*/
|
|
1390
|
+
async availableInTenant(skuId, tenantId) {
|
|
1391
|
+
const rows = await readOwnAndGlobal(tenantId, () => this.stockService.levels.list({ where: {
|
|
1392
|
+
skuId,
|
|
1393
|
+
state: "available"
|
|
1394
|
+
} }));
|
|
1395
|
+
let total = 0;
|
|
1396
|
+
for (const row of rows) if (isOwnOrGlobal(row, tenantId)) total += Number(row.qty ?? 0);
|
|
1397
|
+
return Math.max(0, total);
|
|
1398
|
+
}
|
|
1399
|
+
/** {@link computeMaterialCost} with `levels` above 1. */
|
|
1400
|
+
async computeMultiLevelCost(bomId, options) {
|
|
1401
|
+
const top = await this.requireBom(bomId);
|
|
1402
|
+
const currency = top.currency || "USD";
|
|
1403
|
+
const walk = new StructureWalk(this.assemblies, top, options.levels ?? 1);
|
|
1404
|
+
const rollUp = async (billId, level, path) => {
|
|
1405
|
+
const lineBreakdown = [];
|
|
1406
|
+
let totalCost = 0;
|
|
1407
|
+
let hasMissingCosts = false;
|
|
1408
|
+
for (const line of await walk.linesOf(billId)) {
|
|
1409
|
+
const component = await walk.resolve(line.componentSkuId);
|
|
1410
|
+
const sub = walk.open(component, level, path);
|
|
1411
|
+
const effectiveQty = line.effectiveQtyPerUnit();
|
|
1412
|
+
const entry = {
|
|
1413
|
+
componentSkuId: line.componentSkuId,
|
|
1414
|
+
qtyPerUnit: Number(line.qtyPerUnit ?? 0),
|
|
1415
|
+
wastePercent: Number(line.wastePercent ?? 0),
|
|
1416
|
+
effectiveQty,
|
|
1417
|
+
unitCost: 0,
|
|
1418
|
+
lineCost: 0,
|
|
1419
|
+
uom: line.uom,
|
|
1420
|
+
costUnavailable: false
|
|
1421
|
+
};
|
|
1422
|
+
const sameCurrency = (component.activeBom?.currency || "USD") === currency;
|
|
1423
|
+
if (sub && sameCurrency) {
|
|
1424
|
+
const inner = await rollUp(sub.bomId, level + 1, [...path, sub]);
|
|
1425
|
+
entry.unitCost = inner.totalCost;
|
|
1426
|
+
entry.lineCost = inner.totalCost * effectiveQty;
|
|
1427
|
+
entry.costUnavailable = inner.hasMissingCosts;
|
|
1428
|
+
entry.subBomId = sub.bomId;
|
|
1429
|
+
entry.components = inner.lineBreakdown;
|
|
1430
|
+
totalCost += entry.lineCost;
|
|
1431
|
+
} else {
|
|
1432
|
+
const resolved = await this.resolveCost(line.componentSkuId);
|
|
1433
|
+
entry.unitCost = resolved ?? 0;
|
|
1434
|
+
entry.lineCost = entry.unitCost * effectiveQty;
|
|
1435
|
+
entry.costUnavailable = resolved === null;
|
|
1436
|
+
if (!entry.costUnavailable) totalCost += entry.lineCost;
|
|
1437
|
+
}
|
|
1438
|
+
if (entry.costUnavailable) hasMissingCosts = true;
|
|
1439
|
+
lineBreakdown.push(entry);
|
|
1440
|
+
}
|
|
1441
|
+
return {
|
|
1442
|
+
totalCost,
|
|
1443
|
+
lineBreakdown,
|
|
1444
|
+
hasMissingCosts
|
|
1445
|
+
};
|
|
1446
|
+
};
|
|
1447
|
+
return {
|
|
1448
|
+
bomId,
|
|
1449
|
+
currency,
|
|
1450
|
+
...await rollUp(bomId, 1, [await walk.root()])
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
/** {@link computeLabourEstimate} with `levels` above 1. */
|
|
1454
|
+
async computeMultiLevelLabour(bomId, options) {
|
|
1455
|
+
const top = await this.requireBom(bomId);
|
|
1456
|
+
const walk = new StructureWalk(this.assemblies, top, options.levels ?? 1);
|
|
1457
|
+
const own = await this.labourFor(top);
|
|
1458
|
+
const estimates = /* @__PURE__ */ new Map();
|
|
1459
|
+
const subAssemblies = [];
|
|
1460
|
+
const visit = async (billId, level, path, units) => {
|
|
1461
|
+
for (const line of await walk.linesOf(billId)) {
|
|
1462
|
+
const component = await walk.resolve(line.componentSkuId);
|
|
1463
|
+
const sub = walk.open(component, level, path);
|
|
1464
|
+
if (!sub || !component.activeBom) continue;
|
|
1465
|
+
const subBill = component.activeBom;
|
|
1466
|
+
let estimate = estimates.get(sub.bomId);
|
|
1467
|
+
if (!estimate) {
|
|
1468
|
+
estimate = this.labourFor(subBill);
|
|
1469
|
+
estimates.set(sub.bomId, estimate);
|
|
1470
|
+
}
|
|
1471
|
+
const unitsPerUnit = units * line.effectiveQtyPerUnit();
|
|
1472
|
+
subAssemblies.push({
|
|
1473
|
+
lineId: line.id,
|
|
1474
|
+
componentSkuId: line.componentSkuId,
|
|
1475
|
+
name: sub.name,
|
|
1476
|
+
level,
|
|
1477
|
+
path,
|
|
1478
|
+
unitsPerUnit,
|
|
1479
|
+
estimate: await estimate
|
|
1480
|
+
});
|
|
1481
|
+
await visit(sub.bomId, level + 1, [...path, sub], unitsPerUnit);
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
await visit(bomId, 1, [await walk.root()], 1);
|
|
1485
|
+
let totalMinutes = own.totalMinutes;
|
|
1486
|
+
let totalCost = own.totalCost;
|
|
1487
|
+
let hasMissingRates = own.hasMissingRates;
|
|
1488
|
+
for (const sub of subAssemblies) {
|
|
1489
|
+
totalMinutes += sub.unitsPerUnit * sub.estimate.totalMinutes;
|
|
1490
|
+
if (sub.estimate.currency === own.currency) {
|
|
1491
|
+
totalCost += sub.unitsPerUnit * sub.estimate.totalCost;
|
|
1492
|
+
if (sub.estimate.hasMissingRates) hasMissingRates = true;
|
|
1493
|
+
} else if (sub.estimate.hasRouting) hasMissingRates = true;
|
|
1494
|
+
}
|
|
1495
|
+
return {
|
|
1496
|
+
...own,
|
|
1497
|
+
hasRouting: own.hasRouting || subAssemblies.some((sub) => sub.estimate.hasRouting),
|
|
1498
|
+
totalMinutes,
|
|
1499
|
+
totalCost,
|
|
1500
|
+
hasMissingRates,
|
|
1501
|
+
subAssemblies
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
896
1504
|
/** Run the {@link OperationRateResolver}; `null` when there is no rate. */
|
|
897
1505
|
async resolveRate(operation) {
|
|
898
1506
|
if (!this.rateResolver) return null;
|
|
@@ -913,6 +1521,40 @@ var BomService = class BomService {
|
|
|
913
1521
|
return Number(value);
|
|
914
1522
|
}
|
|
915
1523
|
};
|
|
1524
|
+
function explodedLine(line, component, level, path, parentQty) {
|
|
1525
|
+
const effectiveQtyPerUnit = line.effectiveQtyPerUnit();
|
|
1526
|
+
return {
|
|
1527
|
+
lineId: line.id,
|
|
1528
|
+
bomId: line.bomId,
|
|
1529
|
+
level,
|
|
1530
|
+
path,
|
|
1531
|
+
componentSkuId: line.componentSkuId,
|
|
1532
|
+
kind: component.kind,
|
|
1533
|
+
name: component.product?.name ?? "",
|
|
1534
|
+
skuCode: component.sku?.code ?? "",
|
|
1535
|
+
qtyPerUnit: Number(line.qtyPerUnit ?? 0),
|
|
1536
|
+
wastePercent: Number(line.wastePercent ?? 0),
|
|
1537
|
+
effectiveQtyPerUnit,
|
|
1538
|
+
uom: line.uom,
|
|
1539
|
+
totalQty: parentQty * effectiveQtyPerUnit,
|
|
1540
|
+
buildable: component.buildable,
|
|
1541
|
+
subBomId: component.activeBom?.id ?? null,
|
|
1542
|
+
expanded: false
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
function sumRequirements(lines) {
|
|
1546
|
+
const totals = /* @__PURE__ */ new Map();
|
|
1547
|
+
for (const line of lines) {
|
|
1548
|
+
const existing = totals.get(line.componentSkuId);
|
|
1549
|
+
if (existing) existing.totalQty += line.totalQty;
|
|
1550
|
+
else totals.set(line.componentSkuId, {
|
|
1551
|
+
componentSkuId: line.componentSkuId,
|
|
1552
|
+
totalQty: line.totalQty,
|
|
1553
|
+
uom: line.uom
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
return Array.from(totals.values());
|
|
1557
|
+
}
|
|
916
1558
|
function assertPositiveQty$1(qty, op) {
|
|
917
1559
|
if (!Number.isFinite(qty) || qty <= 0) throw new Error(`${op}: qty must be a positive finite number (got ${qty})`);
|
|
918
1560
|
}
|
|
@@ -1031,13 +1673,14 @@ var ProductionService = class ProductionService {
|
|
|
1031
1673
|
const orderId = order.id ?? "";
|
|
1032
1674
|
if (!orderId) throw new Error("consumeMaterials: order.id is required for source attribution");
|
|
1033
1675
|
const bom = await this.resolveBom(order);
|
|
1676
|
+
const owner = bom.tenantId ?? null;
|
|
1034
1677
|
return {
|
|
1035
1678
|
orderId,
|
|
1036
|
-
lines: await this.lines.findByBom(bom.id),
|
|
1679
|
+
lines: (await readOwnAndGlobal(owner, () => this.lines.findByBom(bom.id))).filter((line) => isOwnOrGlobal(line, owner)),
|
|
1037
1680
|
runQty: options.qty,
|
|
1038
1681
|
locationId: options.locationId,
|
|
1039
1682
|
mutationOptions: {
|
|
1040
|
-
sourceType: "ProductionOrder",
|
|
1683
|
+
sourceType: options.sourceType ?? "ProductionOrder",
|
|
1041
1684
|
sourceId: orderId,
|
|
1042
1685
|
reasonCode: options.reasonCode ?? "production_consume",
|
|
1043
1686
|
note: options.note
|
|
@@ -1101,8 +1744,13 @@ var ProductionService = class ProductionService {
|
|
|
1101
1744
|
*/
|
|
1102
1745
|
async resolveBom(order) {
|
|
1103
1746
|
if (order.bomId) {
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1747
|
+
const bomId = order.bomId;
|
|
1748
|
+
const tenantId = getCurrentTenant()?.tenantId;
|
|
1749
|
+
const bom = tenantId ? (await readOwnAndGlobal(tenantId, () => this.boms.list({
|
|
1750
|
+
where: { id: bomId },
|
|
1751
|
+
limit: 1
|
|
1752
|
+
}))).find((row) => isOwnOrGlobal(row, tenantId)) ?? null : await this.boms.get(bomId);
|
|
1753
|
+
if (!bom) throw new BomNotFoundError(bomId);
|
|
1106
1754
|
return bom;
|
|
1107
1755
|
}
|
|
1108
1756
|
const productId = order.productId ?? "";
|
|
@@ -1124,7 +1772,7 @@ function prepareProduce(order, options) {
|
|
|
1124
1772
|
qty: options.qty,
|
|
1125
1773
|
locationId: options.locationId,
|
|
1126
1774
|
mutationOptions: {
|
|
1127
|
-
sourceType: "ProductionOrder",
|
|
1775
|
+
sourceType: options.sourceType ?? "ProductionOrder",
|
|
1128
1776
|
sourceId: orderId,
|
|
1129
1777
|
reasonCode: options.reasonCode ?? "production_produce",
|
|
1130
1778
|
note: options.note
|
|
@@ -1329,6 +1977,249 @@ async function createOperationService(options) {
|
|
|
1329
1977
|
return OperationService.create(options);
|
|
1330
1978
|
}
|
|
1331
1979
|
//#endregion
|
|
1980
|
+
//#region src/services/ProductionRunService.ts
|
|
1981
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1982
|
+
var OPEN_STATUSES = /* @__PURE__ */ new Set(["planned", "in_progress"]);
|
|
1983
|
+
var ProductionRunService = class ProductionRunService {
|
|
1984
|
+
constructor(stockService, runs, completions, boms) {
|
|
1985
|
+
this.stockService = stockService;
|
|
1986
|
+
this.runs = runs;
|
|
1987
|
+
this.completions = completions;
|
|
1988
|
+
this.boms = boms;
|
|
1989
|
+
}
|
|
1990
|
+
stockService;
|
|
1991
|
+
runs;
|
|
1992
|
+
completions;
|
|
1993
|
+
boms;
|
|
1994
|
+
/** Factory — prefer {@link createProductionRunService}. */
|
|
1995
|
+
static async create(options) {
|
|
1996
|
+
let stockService = options.stockService;
|
|
1997
|
+
if (!stockService) {
|
|
1998
|
+
if (!options.db) throw new Error("ProductionRunService.create: either `db` or `stockService` is required");
|
|
1999
|
+
stockService = await createStockService({ db: await resolveDatabase(options.db) });
|
|
2000
|
+
}
|
|
2001
|
+
const db = stockService.db;
|
|
2002
|
+
if (typeof db.transaction !== "function") throw new Error("ProductionRunService requires a database adapter with transaction() support.");
|
|
2003
|
+
const [runs, completions, boms] = await Promise.all([
|
|
2004
|
+
ProductionRunCollection.create({ db }),
|
|
2005
|
+
ProductionRunCompletionCollection.create({ db }),
|
|
2006
|
+
BillOfMaterialsCollection.create({ db })
|
|
2007
|
+
]);
|
|
2008
|
+
return new ProductionRunService(stockService, runs, completions, boms);
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Create a `planned` run of `targetQty` units, pinned to `bomId`, or to the
|
|
2012
|
+
* active bill of `productId`.
|
|
2013
|
+
*
|
|
2014
|
+
* @throws {InvalidProductionRunInputError} for a target that is not a
|
|
2015
|
+
* positive number, or when neither `bomId` nor `productId` is given.
|
|
2016
|
+
* @throws {BomNotFoundError} when `bomId` does not exist.
|
|
2017
|
+
* @throws {NoActiveBomForProductError} when `productId` has no active bill.
|
|
2018
|
+
*/
|
|
2019
|
+
async createRun(input) {
|
|
2020
|
+
const targetQty = positive(input.targetQty, "targetQty");
|
|
2021
|
+
const tenantId = getCurrentTenant()?.tenantId ?? null;
|
|
2022
|
+
let bom = null;
|
|
2023
|
+
if (input.bomId) {
|
|
2024
|
+
const bomId = input.bomId;
|
|
2025
|
+
bom = (UUID_PATTERN.test(bomId) ? await readOwnAndGlobal(tenantId, () => this.boms.list({
|
|
2026
|
+
where: { id: bomId },
|
|
2027
|
+
limit: 1
|
|
2028
|
+
})) : []).find((row) => isOwnOrGlobal(row, tenantId)) ?? null;
|
|
2029
|
+
if (!bom?.id) throw new BomNotFoundError(bomId);
|
|
2030
|
+
} else if (input.productId) {
|
|
2031
|
+
const productId = input.productId;
|
|
2032
|
+
const active = (await readOwnAndGlobal(tenantId, () => this.boms.list({
|
|
2033
|
+
where: {
|
|
2034
|
+
productId,
|
|
2035
|
+
status: "active"
|
|
2036
|
+
},
|
|
2037
|
+
orderBy: "version DESC"
|
|
2038
|
+
}))).filter((candidate) => isOwnOrGlobal(candidate, tenantId));
|
|
2039
|
+
bom = active.find((candidate) => (candidate.tenantId ?? null) === tenantId) ?? active[0] ?? null;
|
|
2040
|
+
if (!bom?.id) throw new NoActiveBomForProductError(input.productId);
|
|
2041
|
+
} else throw new InvalidProductionRunInputError("A production run needs a bomId or a productId.");
|
|
2042
|
+
return this.runs.create({
|
|
2043
|
+
tenantId: bom.tenantId ?? tenantId,
|
|
2044
|
+
bomId: bom.id,
|
|
2045
|
+
targetQty,
|
|
2046
|
+
completedQty: 0,
|
|
2047
|
+
status: "planned"
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
/** The run, or `null` when no run has this id. */
|
|
2051
|
+
async get(runId) {
|
|
2052
|
+
if (!UUID_PATTERN.test(runId)) return null;
|
|
2053
|
+
return this.runs.get({ id: runId });
|
|
2054
|
+
}
|
|
2055
|
+
/** A run's completions in the order they happened. */
|
|
2056
|
+
async listCompletions(runId) {
|
|
2057
|
+
if (!UUID_PATTERN.test(runId)) return [];
|
|
2058
|
+
return this.completions.findByRun(runId);
|
|
2059
|
+
}
|
|
2060
|
+
/** Mark a `planned` run `in_progress`. */
|
|
2061
|
+
async start(runId) {
|
|
2062
|
+
return this.change(runId, "start", (run) => {
|
|
2063
|
+
if (run.status !== "planned") throw new ProductionRunStateError(runId, run.status, "start");
|
|
2064
|
+
run.status = "in_progress";
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
/** Mark an open run `done`, short of its target if work stopped early. */
|
|
2068
|
+
async finish(runId) {
|
|
2069
|
+
return this.change(runId, "finish", (run) => {
|
|
2070
|
+
run.status = "done";
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
/** Cancel an open run. Completions already reported stay. */
|
|
2074
|
+
async cancel(runId) {
|
|
2075
|
+
return this.change(runId, "cancel", (run) => {
|
|
2076
|
+
run.status = "cancelled";
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
/**
|
|
2080
|
+
* Change an open run's target. It may not go below what is already done;
|
|
2081
|
+
* setting it to exactly that finishes the run.
|
|
2082
|
+
*
|
|
2083
|
+
* @throws {InvalidProductionRunInputError} for a target that is not a
|
|
2084
|
+
* positive number or is below the completed quantity.
|
|
2085
|
+
*/
|
|
2086
|
+
async setTarget(runId, targetQty) {
|
|
2087
|
+
const target = positive(targetQty, "targetQty");
|
|
2088
|
+
return this.change(runId, "retarget", (run) => {
|
|
2089
|
+
const completed = toMillionths(Number(run.completedQty));
|
|
2090
|
+
const goal = toMillionths(target);
|
|
2091
|
+
if (goal < completed) throw new InvalidProductionRunInputError(`The target cannot be below the ${fromMillionths(completed)} already done.`);
|
|
2092
|
+
run.targetQty = target;
|
|
2093
|
+
if (completed > 0 && completed >= goal) run.status = "done";
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
2097
|
+
* Report `qty` finished units on an open run: records a dated completion,
|
|
2098
|
+
* adds it to `completedQty`, moves a `planned` run to `in_progress`, and
|
|
2099
|
+
* marks the run `done` when the target is reached. With `consume` and/or
|
|
2100
|
+
* `produce`, also writes the stock movements through
|
|
2101
|
+
* {@link ProductionService}, stamped `sourceType: 'ProductionRunCompletion'`
|
|
2102
|
+
* with the completion id. Everything runs in one transaction.
|
|
2103
|
+
*
|
|
2104
|
+
* Consumption takes the run's bill's own lines for `qty` units: a
|
|
2105
|
+
* sub-assembly on the bill is taken from stock as a component. Building
|
|
2106
|
+
* that sub-assembly is its own run.
|
|
2107
|
+
*
|
|
2108
|
+
* @throws {ProductionRunNotFoundError}
|
|
2109
|
+
* @throws {ProductionRunStateError} when the run is `done` or `cancelled`.
|
|
2110
|
+
* @throws {ProductionRunOverCompletionError} when `qty` is more than the
|
|
2111
|
+
* run has left.
|
|
2112
|
+
* @throws {InvalidProductionRunInputError} for a bad `qty` or date.
|
|
2113
|
+
* @throws `InsufficientStockError` from inventory when consuming would
|
|
2114
|
+
* drive stock below zero; nothing is recorded then.
|
|
2115
|
+
*/
|
|
2116
|
+
async recordCompletion(runId, input) {
|
|
2117
|
+
const qty = positive(input.qty, "qty");
|
|
2118
|
+
const completedAt = input.completedAt === void 0 ? /* @__PURE__ */ new Date() : new Date(input.completedAt);
|
|
2119
|
+
if (Number.isNaN(completedAt.getTime())) throw new InvalidProductionRunInputError("completedAt is not a valid date.");
|
|
2120
|
+
return this.inTransaction(async (tx) => {
|
|
2121
|
+
const run = await this.load(tx, runId);
|
|
2122
|
+
if (!OPEN_STATUSES.has(run.status)) throw new ProductionRunStateError(runId, run.status, "complete");
|
|
2123
|
+
const target = toMillionths(Number(run.targetQty));
|
|
2124
|
+
const done = toMillionths(Number(run.completedQty));
|
|
2125
|
+
const reported = toMillionths(qty);
|
|
2126
|
+
const remaining = Math.max(0, target - done);
|
|
2127
|
+
if (reported > remaining) throw new ProductionRunOverCompletionError(runId, qty, fromMillionths(remaining));
|
|
2128
|
+
const total = done + reported;
|
|
2129
|
+
run.completedQty = fromMillionths(total);
|
|
2130
|
+
run.status = total >= target ? "done" : "in_progress";
|
|
2131
|
+
await run.save();
|
|
2132
|
+
const completion = await tx.completions.create({
|
|
2133
|
+
tenantId: run.tenantId ?? null,
|
|
2134
|
+
runId,
|
|
2135
|
+
qty,
|
|
2136
|
+
completedAt
|
|
2137
|
+
});
|
|
2138
|
+
let consumed = [];
|
|
2139
|
+
let produced = null;
|
|
2140
|
+
const inRunTenant = (work) => run.tenantId ? withTenant({ tenantId: run.tenantId }, work) : work();
|
|
2141
|
+
if (input.consume || input.produce) {
|
|
2142
|
+
const production = await ProductionService.create({ stockService: tx.stock });
|
|
2143
|
+
const ref = {
|
|
2144
|
+
id: completion.id,
|
|
2145
|
+
bomId: run.bomId
|
|
2146
|
+
};
|
|
2147
|
+
const sourceType = "ProductionRunCompletion";
|
|
2148
|
+
const consume = input.consume && {
|
|
2149
|
+
...input.consume,
|
|
2150
|
+
qty,
|
|
2151
|
+
sourceType
|
|
2152
|
+
};
|
|
2153
|
+
const produce = input.produce && {
|
|
2154
|
+
...input.produce,
|
|
2155
|
+
qty,
|
|
2156
|
+
sourceType
|
|
2157
|
+
};
|
|
2158
|
+
if (consume && produce) ({consumed, produced} = await inRunTenant(() => production.runProduction(ref, {
|
|
2159
|
+
consume,
|
|
2160
|
+
produce
|
|
2161
|
+
})));
|
|
2162
|
+
else if (consume) consumed = await inRunTenant(() => production.consumeMaterials(ref, consume));
|
|
2163
|
+
else if (produce) produced = await inRunTenant(() => production.produceFinishedGoods(ref, produce));
|
|
2164
|
+
}
|
|
2165
|
+
return {
|
|
2166
|
+
run,
|
|
2167
|
+
completion,
|
|
2168
|
+
consumed,
|
|
2169
|
+
produced
|
|
2170
|
+
};
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
/** Apply `apply` to an open run and save it, in one transaction. */
|
|
2174
|
+
change(runId, action, apply) {
|
|
2175
|
+
return this.inTransaction(async (tx) => {
|
|
2176
|
+
const run = await this.load(tx, runId);
|
|
2177
|
+
if (!OPEN_STATUSES.has(run.status)) throw new ProductionRunStateError(runId, run.status, action);
|
|
2178
|
+
apply(run);
|
|
2179
|
+
await run.save();
|
|
2180
|
+
return run;
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
async load(tx, runId) {
|
|
2184
|
+
const run = UUID_PATTERN.test(runId) ? await tx.runs.get({ id: runId }) : null;
|
|
2185
|
+
if (!run) throw new ProductionRunNotFoundError(runId);
|
|
2186
|
+
return run;
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Run `work` in one transaction on collections bound to it, rerunning the
|
|
2190
|
+
* whole transaction when it lost a revision race on the run row.
|
|
2191
|
+
*/
|
|
2192
|
+
inTransaction(work) {
|
|
2193
|
+
return withRevisionRetry$1(() => this.stockService.withTransaction(async (stock) => {
|
|
2194
|
+
const db = stock.db;
|
|
2195
|
+
const [runs, completions] = await Promise.all([ProductionRunCollection.create({ db }), ProductionRunCompletionCollection.create({ db })]);
|
|
2196
|
+
return work({
|
|
2197
|
+
stock,
|
|
2198
|
+
runs,
|
|
2199
|
+
completions
|
|
2200
|
+
});
|
|
2201
|
+
}));
|
|
2202
|
+
}
|
|
2203
|
+
};
|
|
2204
|
+
var MAX_REVISION_ATTEMPTS = 10;
|
|
2205
|
+
async function withRevisionRetry$1(run) {
|
|
2206
|
+
for (let attempt = 1;; attempt++) try {
|
|
2207
|
+
return await run();
|
|
2208
|
+
} catch (error) {
|
|
2209
|
+
if (error?.code !== "RUNTIME_REVISION_CONFLICT" || attempt >= MAX_REVISION_ATTEMPTS) throw error;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function positive(value, field) {
|
|
2213
|
+
const raw = Number(value);
|
|
2214
|
+
const number = Number.isFinite(raw) ? roundQuantity(raw) : raw;
|
|
2215
|
+
if (!Number.isFinite(number) || number <= 0) throw new InvalidProductionRunInputError(`${field} must be a number greater than zero (got ${String(value)}).`);
|
|
2216
|
+
if (number > 999999999) throw new InvalidProductionRunInputError(`${field} must be at most ${MAX_QUANTITY} (got ${String(value)}).`);
|
|
2217
|
+
return number;
|
|
2218
|
+
}
|
|
2219
|
+
function createProductionRunService(options) {
|
|
2220
|
+
return ProductionRunService.create(options);
|
|
2221
|
+
}
|
|
2222
|
+
//#endregion
|
|
1332
2223
|
//#region src/services/RoutingService.ts
|
|
1333
2224
|
var RoutingService = class RoutingService {
|
|
1334
2225
|
constructor(db, boms, operations, steps) {
|
|
@@ -1420,6 +2311,6 @@ async function createRoutingService(options) {
|
|
|
1420
2311
|
return RoutingService.create(options);
|
|
1421
2312
|
}
|
|
1422
2313
|
//#endregion
|
|
1423
|
-
export { Assembly, AssemblyCollection, AssemblyService, BillOfMaterials, BillOfMaterialsCollection, BomCycleError, BomLine, BomLineCollection, BomNotFoundError, BomService, DuplicateOperationCodeError, InvalidOperationInputError, NoActiveBomForProductError, Operation, OperationCollection, OperationNotFoundError, OperationRetiredError, OperationService, ProductionService, RoutingService, RoutingStep, RoutingStepCollection, createAssemblyService, createBomService, createOperationService, createProductionService, createRoutingService, installManufacturingDispatchHandlers };
|
|
2314
|
+
export { Assembly, AssemblyCollection, AssemblyService, BillOfMaterials, BillOfMaterialsCollection, BomCycleError, BomExplosionLimitError, BomLine, BomLineCollection, BomNotFoundError, BomService, BomStructureCycleError, DuplicateOperationCodeError, InvalidOperationInputError, InvalidProductionRunInputError, MAX_EXPLOSION_DEPTH, MAX_EXPLOSION_LINES, MAX_QUANTITY, NoActiveBomForProductError, Operation, OperationCollection, OperationNotFoundError, OperationRetiredError, OperationService, PRODUCTION_RUN_STATUSES, ProductionRun, ProductionRunCollection, ProductionRunCompletion, ProductionRunCompletionCollection, ProductionRunNotFoundError, ProductionRunOverCompletionError, ProductionRunService, ProductionRunStateError, ProductionService, QUANTITY_DECIMALS, RoutingService, RoutingStep, RoutingStepCollection, createAssemblyService, createBomService, createOperationService, createProductionRunService, createProductionService, createRoutingService, installManufacturingDispatchHandlers, roundQuantity };
|
|
1424
2315
|
|
|
1425
2316
|
//# sourceMappingURL=index.js.map
|