@happyvertical/smrt-inventory 0.54.2 → 0.54.4
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 +16 -0
- package/README.md +58 -0
- package/dist/collections/StockLevelCollection.d.ts +5 -0
- package/dist/collections/StockLevelCollection.d.ts.map +1 -1
- package/dist/index.js +91 -12
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +88 -10
- package/dist/models/StockLevel.d.ts +6 -0
- package/dist/models/StockLevel.d.ts.map +1 -1
- package/dist/models/StockMovement.d.ts +3 -0
- package/dist/models/StockMovement.d.ts.map +1 -1
- package/dist/services/StockService.d.ts +9 -0
- package/dist/services/StockService.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +95 -10
- package/dist/svelte/AdjustStockForm.svelte +59 -0
- package/dist/svelte/AdjustStockForm.svelte.d.ts +11 -0
- package/dist/svelte/AdjustStockForm.svelte.d.ts.map +1 -0
- package/dist/svelte/LocationForm.svelte +65 -0
- package/dist/svelte/LocationForm.svelte.d.ts +10 -0
- package/dist/svelte/LocationForm.svelte.d.ts.map +1 -0
- package/dist/svelte/LocationList.svelte +16 -0
- package/dist/svelte/LocationList.svelte.d.ts +9 -0
- package/dist/svelte/LocationList.svelte.d.ts.map +1 -0
- package/dist/svelte/MovementHistory.svelte +14 -0
- package/dist/svelte/MovementHistory.svelte.d.ts +8 -0
- package/dist/svelte/MovementHistory.svelte.d.ts.map +1 -0
- package/dist/svelte/StockLevels.svelte +22 -0
- package/dist/svelte/StockLevels.svelte.d.ts +9 -0
- package/dist/svelte/StockLevels.svelte.d.ts.map +1 -0
- package/dist/svelte/index.d.ts +7 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +5 -0
- package/dist/svelte/types.d.ts +40 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +1 -0
- package/package.json +28 -10
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-inventory",
|
|
5
|
-
"packageVersion": "0.54.
|
|
5
|
+
"packageVersion": "0.54.4",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-inventory:InventoryLocationCollection": {
|
|
8
8
|
"name": "inventorylocationcollection",
|
|
@@ -234,6 +234,20 @@
|
|
|
234
234
|
"isStatic": false,
|
|
235
235
|
"isPublic": true
|
|
236
236
|
},
|
|
237
|
+
"findBelowReorderPoint": {
|
|
238
|
+
"name": "findBelowReorderPoint",
|
|
239
|
+
"async": true,
|
|
240
|
+
"parameters": [
|
|
241
|
+
{
|
|
242
|
+
"name": "locationId",
|
|
243
|
+
"type": "string",
|
|
244
|
+
"optional": true
|
|
245
|
+
}
|
|
246
|
+
],
|
|
247
|
+
"returnType": "Promise<StockLevel[]>",
|
|
248
|
+
"isStatic": false,
|
|
249
|
+
"isPublic": true
|
|
250
|
+
},
|
|
237
251
|
"totalForSku": {
|
|
238
252
|
"name": "totalForSku",
|
|
239
253
|
"async": true,
|
|
@@ -282,7 +296,7 @@
|
|
|
282
296
|
"collectionExportName": "StockLevelCollectionCollection",
|
|
283
297
|
"schema": {
|
|
284
298
|
"tableName": "inventory_stock_levels",
|
|
285
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_levels\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"state\" TEXT NOT NULL DEFAULT 'available',\n \"qty\" REAL\n);",
|
|
299
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_levels\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"state\" TEXT NOT NULL DEFAULT 'available',\n \"qty\" REAL,\n \"reorder_point\" REAL,\n \"reorder_quantity\" REAL\n);",
|
|
286
300
|
"columns": {
|
|
287
301
|
"id": {
|
|
288
302
|
"type": "UUID",
|
|
@@ -337,6 +351,16 @@
|
|
|
337
351
|
"type": "REAL",
|
|
338
352
|
"notNull": false,
|
|
339
353
|
"unique": false
|
|
354
|
+
},
|
|
355
|
+
"reorder_point": {
|
|
356
|
+
"type": "REAL",
|
|
357
|
+
"notNull": false,
|
|
358
|
+
"unique": false
|
|
359
|
+
},
|
|
360
|
+
"reorder_quantity": {
|
|
361
|
+
"type": "REAL",
|
|
362
|
+
"notNull": false,
|
|
363
|
+
"unique": false
|
|
340
364
|
}
|
|
341
365
|
},
|
|
342
366
|
"indexes": [
|
|
@@ -366,7 +390,7 @@
|
|
|
366
390
|
]
|
|
367
391
|
}
|
|
368
392
|
],
|
|
369
|
-
"version": "
|
|
393
|
+
"version": "02387935"
|
|
370
394
|
},
|
|
371
395
|
"importPath": "@happyvertical/smrt-inventory"
|
|
372
396
|
},
|
|
@@ -450,7 +474,7 @@
|
|
|
450
474
|
"collectionExportName": "StockMovementCollectionCollection",
|
|
451
475
|
"schema": {
|
|
452
476
|
"tableName": "inventory_stock_movements",
|
|
453
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_movements\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"from_state\" TEXT,\n \"to_state\" TEXT,\n \"qty\" REAL,\n \"reason_code\" TEXT NOT NULL DEFAULT 'adjustment',\n \"source_type\" TEXT DEFAULT '',\n \"source_id\" TEXT DEFAULT '',\n \"note\" TEXT DEFAULT '',\n \"occurred_at\" TIMESTAMP\n);",
|
|
477
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_movements\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"from_state\" TEXT,\n \"to_state\" TEXT,\n \"qty\" REAL,\n \"reason_code\" TEXT NOT NULL DEFAULT 'adjustment',\n \"source_type\" TEXT DEFAULT '',\n \"source_id\" TEXT DEFAULT '',\n \"actor_profile_id\" UUID,\n \"note\" TEXT DEFAULT '',\n \"occurred_at\" TIMESTAMP\n);",
|
|
454
478
|
"columns": {
|
|
455
479
|
"id": {
|
|
456
480
|
"type": "UUID",
|
|
@@ -528,6 +552,12 @@
|
|
|
528
552
|
"unique": false,
|
|
529
553
|
"default": ""
|
|
530
554
|
},
|
|
555
|
+
"actor_profile_id": {
|
|
556
|
+
"type": "UUID",
|
|
557
|
+
"referenceKind": "crossPackageRef",
|
|
558
|
+
"notNull": false,
|
|
559
|
+
"unique": false
|
|
560
|
+
},
|
|
531
561
|
"note": {
|
|
532
562
|
"type": "TEXT",
|
|
533
563
|
"notNull": false,
|
|
@@ -554,9 +584,15 @@
|
|
|
554
584
|
"tenant_id",
|
|
555
585
|
"created_at"
|
|
556
586
|
]
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"name": "inventory_stock_movements_actor_profile_id_idx",
|
|
590
|
+
"columns": [
|
|
591
|
+
"actor_profile_id"
|
|
592
|
+
]
|
|
557
593
|
}
|
|
558
594
|
],
|
|
559
|
-
"version": "
|
|
595
|
+
"version": "eff1027e"
|
|
560
596
|
},
|
|
561
597
|
"importPath": "@happyvertical/smrt-inventory"
|
|
562
598
|
},
|
|
@@ -800,6 +836,20 @@
|
|
|
800
836
|
"type": "decimal",
|
|
801
837
|
"required": false,
|
|
802
838
|
"_meta": {}
|
|
839
|
+
},
|
|
840
|
+
"reorderPoint": {
|
|
841
|
+
"type": "decimal",
|
|
842
|
+
"required": false,
|
|
843
|
+
"_meta": {
|
|
844
|
+
"nullable": true
|
|
845
|
+
}
|
|
846
|
+
},
|
|
847
|
+
"reorderQuantity": {
|
|
848
|
+
"type": "decimal",
|
|
849
|
+
"required": false,
|
|
850
|
+
"_meta": {
|
|
851
|
+
"nullable": true
|
|
852
|
+
}
|
|
803
853
|
}
|
|
804
854
|
},
|
|
805
855
|
"methods": {},
|
|
@@ -855,7 +905,7 @@
|
|
|
855
905
|
],
|
|
856
906
|
"schema": {
|
|
857
907
|
"tableName": "inventory_stock_levels",
|
|
858
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_levels\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"state\" TEXT NOT NULL DEFAULT 'available',\n \"qty\" REAL\n);",
|
|
908
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_levels\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"state\" TEXT NOT NULL DEFAULT 'available',\n \"qty\" REAL,\n \"reorder_point\" REAL,\n \"reorder_quantity\" REAL\n);",
|
|
859
909
|
"columns": {
|
|
860
910
|
"id": {
|
|
861
911
|
"type": "UUID",
|
|
@@ -910,6 +960,16 @@
|
|
|
910
960
|
"type": "REAL",
|
|
911
961
|
"notNull": false,
|
|
912
962
|
"unique": false
|
|
963
|
+
},
|
|
964
|
+
"reorder_point": {
|
|
965
|
+
"type": "REAL",
|
|
966
|
+
"notNull": false,
|
|
967
|
+
"unique": false
|
|
968
|
+
},
|
|
969
|
+
"reorder_quantity": {
|
|
970
|
+
"type": "REAL",
|
|
971
|
+
"notNull": false,
|
|
972
|
+
"unique": false
|
|
913
973
|
}
|
|
914
974
|
},
|
|
915
975
|
"indexes": [
|
|
@@ -939,7 +999,7 @@
|
|
|
939
999
|
]
|
|
940
1000
|
}
|
|
941
1001
|
],
|
|
942
|
-
"version": "
|
|
1002
|
+
"version": "02387935"
|
|
943
1003
|
},
|
|
944
1004
|
"importPath": "@happyvertical/smrt-inventory"
|
|
945
1005
|
},
|
|
@@ -1016,6 +1076,11 @@
|
|
|
1016
1076
|
"required": false,
|
|
1017
1077
|
"default": ""
|
|
1018
1078
|
},
|
|
1079
|
+
"actorProfileId": {
|
|
1080
|
+
"type": "crossPackageRef",
|
|
1081
|
+
"required": false,
|
|
1082
|
+
"related": "@happyvertical/smrt-profiles:Profile"
|
|
1083
|
+
},
|
|
1019
1084
|
"note": {
|
|
1020
1085
|
"type": "text",
|
|
1021
1086
|
"required": false,
|
|
@@ -1076,7 +1141,7 @@
|
|
|
1076
1141
|
],
|
|
1077
1142
|
"schema": {
|
|
1078
1143
|
"tableName": "inventory_stock_movements",
|
|
1079
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_movements\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"from_state\" TEXT,\n \"to_state\" TEXT,\n \"qty\" REAL,\n \"reason_code\" TEXT NOT NULL DEFAULT 'adjustment',\n \"source_type\" TEXT DEFAULT '',\n \"source_id\" TEXT DEFAULT '',\n \"note\" TEXT DEFAULT '',\n \"occurred_at\" TIMESTAMP\n);",
|
|
1144
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"inventory_stock_movements\" (\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 \"sku_id\" TEXT NOT NULL DEFAULT '',\n \"location_id\" TEXT NOT NULL DEFAULT '',\n \"from_state\" TEXT,\n \"to_state\" TEXT,\n \"qty\" REAL,\n \"reason_code\" TEXT NOT NULL DEFAULT 'adjustment',\n \"source_type\" TEXT DEFAULT '',\n \"source_id\" TEXT DEFAULT '',\n \"actor_profile_id\" UUID,\n \"note\" TEXT DEFAULT '',\n \"occurred_at\" TIMESTAMP\n);",
|
|
1080
1145
|
"columns": {
|
|
1081
1146
|
"id": {
|
|
1082
1147
|
"type": "UUID",
|
|
@@ -1154,6 +1219,12 @@
|
|
|
1154
1219
|
"unique": false,
|
|
1155
1220
|
"default": ""
|
|
1156
1221
|
},
|
|
1222
|
+
"actor_profile_id": {
|
|
1223
|
+
"type": "UUID",
|
|
1224
|
+
"referenceKind": "crossPackageRef",
|
|
1225
|
+
"notNull": false,
|
|
1226
|
+
"unique": false
|
|
1227
|
+
},
|
|
1157
1228
|
"note": {
|
|
1158
1229
|
"type": "TEXT",
|
|
1159
1230
|
"notNull": false,
|
|
@@ -1180,9 +1251,15 @@
|
|
|
1180
1251
|
"tenant_id",
|
|
1181
1252
|
"created_at"
|
|
1182
1253
|
]
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
"name": "inventory_stock_movements_actor_profile_id_idx",
|
|
1257
|
+
"columns": [
|
|
1258
|
+
"actor_profile_id"
|
|
1259
|
+
]
|
|
1183
1260
|
}
|
|
1184
1261
|
],
|
|
1185
|
-
"version": "
|
|
1262
|
+
"version": "eff1027e"
|
|
1186
1263
|
},
|
|
1187
1264
|
"importPath": "@happyvertical/smrt-inventory"
|
|
1188
1265
|
}
|
|
@@ -1191,5 +1268,6 @@
|
|
|
1191
1268
|
"smrtDependencies": [
|
|
1192
1269
|
"@happyvertical/smrt-core",
|
|
1193
1270
|
"@happyvertical/smrt-tenancy"
|
|
1194
|
-
]
|
|
1271
|
+
],
|
|
1272
|
+
"stampsConstructors": true
|
|
1195
1273
|
}
|
|
@@ -26,6 +26,8 @@ export interface StockLevelOptions extends SmrtObjectOptions {
|
|
|
26
26
|
locationId?: string;
|
|
27
27
|
state?: StockState;
|
|
28
28
|
qty?: number;
|
|
29
|
+
reorderPoint?: number | null;
|
|
30
|
+
reorderQuantity?: number | null;
|
|
29
31
|
}
|
|
30
32
|
export declare class StockLevel extends SmrtObject {
|
|
31
33
|
/** Tenant scope. `null` means the level row is global. */
|
|
@@ -42,6 +44,10 @@ export declare class StockLevel extends SmrtObject {
|
|
|
42
44
|
* (kilograms, litres, metres).
|
|
43
45
|
*/
|
|
44
46
|
qty: number;
|
|
47
|
+
/** Available-stock threshold; null disables reorder monitoring. */
|
|
48
|
+
reorderPoint: number | null;
|
|
49
|
+
/** Optional suggested replenishment quantity, independent of stock balance. */
|
|
50
|
+
reorderQuantity: number | null;
|
|
45
51
|
constructor(options?: StockLevelOptions);
|
|
46
52
|
}
|
|
47
53
|
//# sourceMappingURL=StockLevel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockLevel.d.ts","sourceRoot":"","sources":["../../src/models/StockLevel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"StockLevel.d.ts","sourceRoot":"","sources":["../../src/models/StockLevel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,qBAgBa,UAAW,SAAQ,UAAU;IACxC,0DAA0D;IAE1D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,iEAAiE;IAEjE,KAAK,EAAE,MAAM,CAAM;IAEnB,+EAA+E;IAE/E,UAAU,EAAE,MAAM,CAAM;IAExB,6EAA6E;IAE7E,KAAK,EAAE,UAAU,CAAe;IAEhC;;;;OAIG;IAEH,GAAG,EAAE,MAAM,CAAO;IAElB,mEAAmE;IAEnE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC,+EAA+E;IAE/E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAE1B,OAAO,GAAE,iBAAsB;CAY5C"}
|
|
@@ -35,6 +35,7 @@ export interface StockMovementOptions extends SmrtObjectOptions {
|
|
|
35
35
|
reasonCode?: StockMovementReason;
|
|
36
36
|
sourceType?: string;
|
|
37
37
|
sourceId?: string;
|
|
38
|
+
actorProfileId?: string | null;
|
|
38
39
|
note?: string;
|
|
39
40
|
occurredAt?: Date | string;
|
|
40
41
|
}
|
|
@@ -76,6 +77,8 @@ export declare class StockMovement extends SmrtObject {
|
|
|
76
77
|
* the framework never dereferences it.
|
|
77
78
|
*/
|
|
78
79
|
sourceId: string;
|
|
80
|
+
/** Profile responsible for this movement; null for unattributed automation. */
|
|
81
|
+
actorProfileId: string | null;
|
|
79
82
|
/** Optional free-form note shown in audit UIs. */
|
|
80
83
|
note: string;
|
|
81
84
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockMovement.d.ts","sourceRoot":"","sources":["../../src/models/StockMovement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"StockMovement.d.ts","sourceRoot":"","sources":["../../src/models/StockMovement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAGL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC5B;AAED,qBAmBa,aAAc,SAAQ,UAAU;IAC3C,yDAAyD;IAEzD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,6DAA6D;IAE7D,KAAK,EAAE,MAAM,CAAM;IAEnB,6EAA6E;IAE7E,UAAU,EAAE,MAAM,CAAM;IAExB;;;;OAIG;IACH,SAAS,EAAE,UAAU,GAAG,IAAI,CAAQ;IAEpC;;;OAGG;IACH,OAAO,EAAE,UAAU,GAAG,IAAI,CAAQ;IAElC,4EAA4E;IAE5E,GAAG,EAAE,MAAM,CAAO;IAElB;;;;OAIG;IAEH,UAAU,EAAE,mBAAmB,CAAgB;IAE/C;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAM;IAExB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAM;IAEtB,+EAA+E;IAE/E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IAErC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAM;IAElB;;;OAGG;IACH,UAAU,EAAE,IAAI,CAAc;gBAElB,OAAO,GAAE,oBAAyB;CAqB/C"}
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
*/
|
|
42
42
|
import { type DatabaseConfig } from '@happyvertical/smrt-core';
|
|
43
43
|
import { InventoryLocationCollection, StockLevelCollection, StockMovementCollection } from '../collections/index.js';
|
|
44
|
+
import type { StockLevel } from '../models/StockLevel.js';
|
|
44
45
|
import type { StockMovementReason, StockState } from '../types.js';
|
|
45
46
|
/**
|
|
46
47
|
* Thrown by {@link StockService.reserve} (and {@link StockService.fulfill},
|
|
@@ -68,6 +69,8 @@ export interface StockMutationOptions {
|
|
|
68
69
|
sourceType?: string;
|
|
69
70
|
/** Cross-package id of the row that caused this mutation. */
|
|
70
71
|
sourceId?: string;
|
|
72
|
+
/** Trusted actor attribution; null for unattended or legacy callers. */
|
|
73
|
+
actorProfileId?: string | null;
|
|
71
74
|
/** Free-form note shown in audit UIs. */
|
|
72
75
|
note?: string;
|
|
73
76
|
/**
|
|
@@ -205,6 +208,12 @@ export declare class StockService {
|
|
|
205
208
|
* credited).
|
|
206
209
|
*/
|
|
207
210
|
transfer(skuId: string, fromLocationId: string, toLocationId: string, qty: number, options?: StockMutationOptions): Promise<void>;
|
|
211
|
+
/**
|
|
212
|
+
* Configure replenishment for the available row at a SKU/location.
|
|
213
|
+
* Null disables the threshold. Metadata changes never fabricate movements.
|
|
214
|
+
* The optional quantity is replaced (omitting it clears the suggestion).
|
|
215
|
+
*/
|
|
216
|
+
setReorderPolicy(skuId: string, locationId: string, reorderPoint: number | null, reorderQuantity?: number | null): Promise<StockLevel>;
|
|
208
217
|
/**
|
|
209
218
|
* Apply a positive or negative `delta` to a level row. Used for cycle
|
|
210
219
|
* counts and one-off corrections; `delta=+5` adds five units,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockService.d.ts","sourceRoot":"","sources":["../../src/services/StockService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAGH,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,0BAA0B,CAAC;AAChF,OAAO,EACL,2BAA2B,EAC3B,oBAAoB,EACpB,uBAAuB,EACxB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"StockService.d.ts","sourceRoot":"","sources":["../../src/services/StockService.ts"],"names":[],"mappings":"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"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,33 +3,40 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-inventory",
|
|
6
|
-
"packageVersion": "0.54.
|
|
6
|
+
"packageVersion": "0.54.4",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "874078a8386fdd561db1706030671d467922244377177102c365aca39bf704ec",
|
|
11
|
+
"packageJson": "f5479a5e62c6d20d01833b1585a64932b41c4e9b619722727277b31511eaddef",
|
|
12
|
+
"agents": "f1e6618e492c2726dee29c3e3fb203c03a64de184c7992407a1025b12618625d"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
16
16
|
"./manifest",
|
|
17
|
-
"./manifest.json"
|
|
17
|
+
"./manifest.json",
|
|
18
|
+
"./svelte"
|
|
18
19
|
],
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"@happyvertical/logger": "catalog:",
|
|
21
22
|
"@happyvertical/smrt-core": "workspace:*",
|
|
22
23
|
"@happyvertical/smrt-tenancy": "workspace:*",
|
|
23
24
|
"@happyvertical/sql": "catalog:",
|
|
25
|
+
"@happyvertical/smrt-ui": "workspace:*",
|
|
24
26
|
"@happyvertical/smrt-vitest": "workspace:*",
|
|
25
27
|
"@types/node": "24.13.6",
|
|
26
28
|
"typescript": "6.0.3",
|
|
27
29
|
"vite": "8.1.4",
|
|
28
|
-
"vitest": "4.1.10"
|
|
30
|
+
"vitest": "4.1.10",
|
|
31
|
+
"@sveltejs/package": "^2.5.8",
|
|
32
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
33
|
+
"svelte": "^5.57.0",
|
|
34
|
+
"svelte-check": "^4.7.6"
|
|
29
35
|
},
|
|
30
36
|
"smrtDependencies": [
|
|
31
37
|
"@happyvertical/smrt-core",
|
|
32
38
|
"@happyvertical/smrt-tenancy",
|
|
39
|
+
"@happyvertical/smrt-ui",
|
|
33
40
|
"@happyvertical/smrt-vitest"
|
|
34
41
|
],
|
|
35
42
|
"sdkDependencies": [
|
|
@@ -47,6 +54,7 @@
|
|
|
47
54
|
"warehouse"
|
|
48
55
|
],
|
|
49
56
|
"risks": [
|
|
57
|
+
"cross-package-refs:1",
|
|
50
58
|
"sensitive-fields-excluded"
|
|
51
59
|
],
|
|
52
60
|
"objects": [
|
|
@@ -197,6 +205,7 @@
|
|
|
197
205
|
"fields": [],
|
|
198
206
|
"relationships": [],
|
|
199
207
|
"methods": [
|
|
208
|
+
"findBelowReorderPoint",
|
|
200
209
|
"findByLocation",
|
|
201
210
|
"findBySku",
|
|
202
211
|
"getLevel",
|
|
@@ -204,6 +213,14 @@
|
|
|
204
213
|
"totalForSku"
|
|
205
214
|
],
|
|
206
215
|
"methodSignatures": [
|
|
216
|
+
{
|
|
217
|
+
"name": "findBelowReorderPoint",
|
|
218
|
+
"async": true,
|
|
219
|
+
"params": [
|
|
220
|
+
"locationId?: string"
|
|
221
|
+
],
|
|
222
|
+
"returns": "Promise<StockLevel[]>"
|
|
223
|
+
},
|
|
207
224
|
{
|
|
208
225
|
"name": "findByLocation",
|
|
209
226
|
"async": true,
|
|
@@ -274,6 +291,14 @@
|
|
|
274
291
|
"path": "/stocklevels/findByLocation",
|
|
275
292
|
"method": "POST"
|
|
276
293
|
},
|
|
294
|
+
{
|
|
295
|
+
"kind": "api",
|
|
296
|
+
"name": "stocklevels.findBelowReorderPoint",
|
|
297
|
+
"operation": "findBelowReorderPoint",
|
|
298
|
+
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection",
|
|
299
|
+
"path": "/stocklevels/findBelowReorderPoint",
|
|
300
|
+
"method": "POST"
|
|
301
|
+
},
|
|
277
302
|
{
|
|
278
303
|
"kind": "api",
|
|
279
304
|
"name": "stocklevels.totalForSku",
|
|
@@ -308,6 +333,12 @@
|
|
|
308
333
|
"operation": "findByLocation",
|
|
309
334
|
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
310
335
|
},
|
|
336
|
+
{
|
|
337
|
+
"kind": "cli",
|
|
338
|
+
"name": "stocklevelcollection_findBelowReorderPoint",
|
|
339
|
+
"operation": "findBelowReorderPoint",
|
|
340
|
+
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
341
|
+
},
|
|
311
342
|
{
|
|
312
343
|
"kind": "cli",
|
|
313
344
|
"name": "stocklevelcollection_totalForSku",
|
|
@@ -338,6 +369,12 @@
|
|
|
338
369
|
"operation": "findByLocation",
|
|
339
370
|
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
340
371
|
},
|
|
372
|
+
{
|
|
373
|
+
"kind": "mcp",
|
|
374
|
+
"name": "stocklevelcollection_findbelowreorderpoint",
|
|
375
|
+
"operation": "findBelowReorderPoint",
|
|
376
|
+
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
377
|
+
},
|
|
341
378
|
{
|
|
342
379
|
"kind": "mcp",
|
|
343
380
|
"name": "stocklevelcollection_totalforsku",
|
|
@@ -678,6 +715,18 @@
|
|
|
678
715
|
"type": "decimal",
|
|
679
716
|
"required": false,
|
|
680
717
|
"columnType": "REAL"
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
"name": "reorderPoint",
|
|
721
|
+
"type": "decimal",
|
|
722
|
+
"required": false,
|
|
723
|
+
"columnType": "REAL"
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
"name": "reorderQuantity",
|
|
727
|
+
"type": "decimal",
|
|
728
|
+
"required": false,
|
|
729
|
+
"columnType": "REAL"
|
|
681
730
|
}
|
|
682
731
|
],
|
|
683
732
|
"relationships": [],
|
|
@@ -808,6 +857,13 @@
|
|
|
808
857
|
"columnType": "TEXT",
|
|
809
858
|
"default": ""
|
|
810
859
|
},
|
|
860
|
+
{
|
|
861
|
+
"name": "actorProfileId",
|
|
862
|
+
"type": "crossPackageRef",
|
|
863
|
+
"required": false,
|
|
864
|
+
"related": "@happyvertical/smrt-profiles:Profile",
|
|
865
|
+
"columnType": "UUID"
|
|
866
|
+
},
|
|
811
867
|
{
|
|
812
868
|
"name": "note",
|
|
813
869
|
"type": "text",
|
|
@@ -822,7 +878,15 @@
|
|
|
822
878
|
"columnType": "TIMESTAMP"
|
|
823
879
|
}
|
|
824
880
|
],
|
|
825
|
-
"relationships": [
|
|
881
|
+
"relationships": [
|
|
882
|
+
{
|
|
883
|
+
"name": "actorProfileId",
|
|
884
|
+
"type": "crossPackageRef",
|
|
885
|
+
"required": false,
|
|
886
|
+
"related": "@happyvertical/smrt-profiles:Profile",
|
|
887
|
+
"columnType": "UUID"
|
|
888
|
+
}
|
|
889
|
+
],
|
|
826
890
|
"methods": [],
|
|
827
891
|
"tenant": {
|
|
828
892
|
"scoped": true,
|
|
@@ -875,6 +939,7 @@
|
|
|
875
939
|
}
|
|
876
940
|
],
|
|
877
941
|
"relationshipFeatures": [
|
|
942
|
+
"crossPackageRef",
|
|
878
943
|
"uuidColumns"
|
|
879
944
|
],
|
|
880
945
|
"tags": [],
|
|
@@ -986,6 +1051,14 @@
|
|
|
986
1051
|
"path": "/stocklevels/findByLocation",
|
|
987
1052
|
"method": "POST"
|
|
988
1053
|
},
|
|
1054
|
+
{
|
|
1055
|
+
"kind": "api",
|
|
1056
|
+
"name": "stocklevels.findBelowReorderPoint",
|
|
1057
|
+
"operation": "findBelowReorderPoint",
|
|
1058
|
+
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection",
|
|
1059
|
+
"path": "/stocklevels/findBelowReorderPoint",
|
|
1060
|
+
"method": "POST"
|
|
1061
|
+
},
|
|
989
1062
|
{
|
|
990
1063
|
"kind": "api",
|
|
991
1064
|
"name": "stocklevels.totalForSku",
|
|
@@ -1020,6 +1093,12 @@
|
|
|
1020
1093
|
"operation": "findByLocation",
|
|
1021
1094
|
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
1022
1095
|
},
|
|
1096
|
+
{
|
|
1097
|
+
"kind": "cli",
|
|
1098
|
+
"name": "stocklevelcollection_findBelowReorderPoint",
|
|
1099
|
+
"operation": "findBelowReorderPoint",
|
|
1100
|
+
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
1101
|
+
},
|
|
1023
1102
|
{
|
|
1024
1103
|
"kind": "cli",
|
|
1025
1104
|
"name": "stocklevelcollection_totalForSku",
|
|
@@ -1050,6 +1129,12 @@
|
|
|
1050
1129
|
"operation": "findByLocation",
|
|
1051
1130
|
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
1052
1131
|
},
|
|
1132
|
+
{
|
|
1133
|
+
"kind": "mcp",
|
|
1134
|
+
"name": "stocklevelcollection_findbelowreorderpoint",
|
|
1135
|
+
"operation": "findBelowReorderPoint",
|
|
1136
|
+
"objectName": "@happyvertical/smrt-inventory:StockLevelCollection"
|
|
1137
|
+
},
|
|
1053
1138
|
{
|
|
1054
1139
|
"kind": "mcp",
|
|
1055
1140
|
"name": "stocklevelcollection_totalforsku",
|
|
@@ -1300,11 +1385,11 @@
|
|
|
1300
1385
|
"prompts": [],
|
|
1301
1386
|
"relationshipsV2": {
|
|
1302
1387
|
"foreignKeyFields": 0,
|
|
1303
|
-
"crossPackageRefFields":
|
|
1388
|
+
"crossPackageRefFields": 1,
|
|
1304
1389
|
"junctionCollections": 0,
|
|
1305
1390
|
"hierarchicalObjects": 0,
|
|
1306
1391
|
"polymorphicAssociations": 0,
|
|
1307
|
-
"uuidColumns":
|
|
1392
|
+
"uuidColumns": 14
|
|
1308
1393
|
},
|
|
1309
|
-
"agentDoc": "# @happyvertical/smrt-inventory\n\nMulti-location stock tracking. Strictly industry-neutral — the same primitives serve apparel, furniture, automotive, CPG, electronics, and any other vertical that counts discrete units across locations.\n\n## Models\n\n| Model | Purpose |\n|---|---|\n| _(catalog shapes — `Product`, `Material`, `ProductVariant`, `Sku` — live in `@happyvertical/smrt-products`)_ | Sku is the smallest sellable / countable unit; its `attributes` JSON carries axis-value pins. ProductVariant is the per-product axis declaration. Import either from `@happyvertical/smrt-products` directly. |\n| `InventoryLocation` | Warehouse / factory / retail / in-transit / virtual. Open-ended `kind` string. Optional `placeId` references `@happyvertical/smrt-places`. `conflictColumns: ['code', 'tenant_id']`. |\n| `StockLevel` | Materialized `qty` for a `(skuId, locationId, state)` tuple. Upserted in place. **Mutated only by `StockService`.** States: `available`, `allocated`, `wip`, `qc_hold`, `damaged`. |\n| `StockMovement` | Append-only audit log. Every `StockService` mutation writes one (or two for `transfer`). `sourceType` + `sourceId` carry cross-package attribution. |\n\nAll three inventory models (`InventoryLocation`, `StockLevel`, `StockMovement`) use `@TenantScoped({ mode: 'optional' })` + nullable `tenantId` so they can be used either tenant-scoped or globally. The catalog shapes (`Product`, `Material`, `ProductVariant`, `Sku`) live in `@happyvertical/smrt-products` and carry their own tenant decoration there.\n\n## StockService — the only sanctioned way to mutate stock\n\n```typescript\nimport { createStockService } from '@happyvertical/smrt-inventory';\n\nconst service = await createStockService({ db });\nawait service.receive(skuId, locationId, qty, { sourceType, sourceId });\nawait service.reserve(skuId, locationId, qty, { sourceType, sourceId });\nawait service.release(skuId, locationId, qty, { sourceType, sourceId });\nawait service.fulfill(skuId, locationId, qty, { sourceType, sourceId });\nawait service.transfer(skuId, fromLocId, toLocId, qty, { sourceType, sourceId });\nawait service.adjust(skuId, locationId, delta, { sourceType, sourceId });\n```\n\n| Method | Behavior | Movement reason |\n|---|---|---|\n| `receive` | +qty available — purchase receipt, return, production produce | `receipt` |\n| `reserve` | available → allocated. Throws `InsufficientStockError` on overdraw | `reservation` |\n| `release` | allocated → available (order cancel) | `release` |\n| `fulfill` | -qty allocated. Stock leaves the building | `fulfillment` |\n| `transfer` | move stock between locations (writes two movements) | `transfer_out`, `transfer_in` |\n| `adjust` | signed delta — cycle counts and one-off corrections | `adjustment` |\n\nAll methods reject zero / negative / non-finite quantities except `adjust`, which accepts non-zero signed deltas. Negative deltas that would drive a state below zero throw `InsufficientStockError`.\n\n## Opt-in DispatchBus hooks\n\nOff by default. Wire them up explicitly in the application's `smrt.ts`:\n\n```typescript\nimport { installInventoryDispatchHandlers } from '@happyvertical/smrt-inventory';\n\nconst handlers = await installInventoryDispatchHandlers({\n dispatchBus: bus,\n db, // or stockService: existingService\n});\n```\n\nThis subscribes to:\n- `contract:created` → calls `service.reserve()` for every line inside one `stockService.withTransaction(...)`, attributed to `('Contract', payload.contractId)`\n- `fulfillment:shipped` → calls `service.fulfill()` for every line inside one `stockService.withTransaction(...)`, attributed to `('Fulfillment', payload.fulfillmentId)`\n\nBoth handlers are atomic across lines: a shortfall on line N rolls back lines 1..N-1 so the event is all-or-nothing. Without this guarantee, `DispatchBus` would only log the async handler error and a partially-reserved (or partially-fulfilled) contract would have no compensating event to fix it.\n\nPer-handler toggles: `installContractReserved`, `installFulfillmentShipped`. The `production_order:posted` hook is deliberately not installed here — it depends on the BOM model and ships in `@happyvertical/smrt-manufacturing` (issue #1250).\n\n## Schema migration (Phase 1 release)\n\nThe `Sku` model moved out of this package and into `@happyvertical/smrt-products`. Previously it lived in this package's `inventory_skus` table; it now lives in `product_skus` over there.\n\n**Upgrade procedure** for deployed consumers:\n\n1. **Let the framework create the destination table first.** Boot the new version once with `@happyvertical/smrt-products` registered in your `SmrtClassOptions`; the lazy `syncSchema` path will create `product_skus` with the right primary key, NOT NULL, UNIQUE (`code`, `tenant_id`), and indexes — they're derived from the `Sku` model decorators and would be stripped by a raw `CREATE TABLE AS SELECT`.\n\n2. **Idempotently copy rows across.** SQLite + Postgres:\n\n ```sql\n BEGIN;\n INSERT INTO product_skus (\n id, slug, context, created_at, updated_at,\n tenant_id, product_id, code, barcode, name,\n attributes, weight_grams, parent_sku_id, active\n )\n SELECT\n id, slug, context, created_at, updated_at,\n tenant_id, product_id, code, barcode, name,\n attributes, weight_grams, parent_sku_id, active\n FROM inventory_skus\n WHERE NOT EXISTS (\n SELECT 1 FROM product_skus p WHERE p.id = inventory_skus.id\n );\n COMMIT;\n ```\n\n3. **Drop the legacy table** once you've verified row counts match:\n\n ```sql\n DROP TABLE IF EXISTS inventory_skus;\n ```\n\n`StockLevel.skuId` and `StockMovement.skuId` carry plain string ids that still resolve to the same rows after the rename, so no inventory data has to move.\n\n## Gotchas\n\n- **Movements are append-only.** The materialized `StockLevel` row is derived state; the `StockMovement` ledger is the source of truth. Never let CRUD UIs expose update/delete on the movement table. If you find yourself wanting to \"fix\" a movement row, write a compensating `adjust()` call instead.\n- **Never call `StockLevel.create()` or `save()` directly.** Going around `StockService` silently desyncs the audit log and breaks cycle counts.\n- **Cross-industry constraint.** This package's vocabulary stays generic: `InventoryLocation`, `StockLevel`, `StockMovement`. Apparel-specific concepts (`Style`, `Makeup`, `Colorway`, fashion `Season`, tech-pack metadata) and their analogues in furniture / automotive / CPG live in the relevant template package, never here. PRs that introduce industry vocabulary should be rejected.\n- **Cross-package references are plain strings.** `skuId`, `placeId`, `sourceId` — all plain string ids, never `@foreignKey()`. Inventory's stock-motion logic only ever reads StockLevel/StockMovement; it doesn't follow `skuId` back to the catalog's Sku table, so the layering stays loose.\n- **`conflictColumns` include `tenant_id`** on `InventoryLocation` and `StockLevel`. NULL-matching semantics (`(code, NULL) IS NOT DISTINCT FROM (code, NULL)`) are handled by `@happyvertical/sql >= 0.74.0` natively, so two saves with the same `(code, NULL)` tuple correctly merge in place. Pass `nullsDistinct: true` at the sql layer to opt back into NULL-distinct semantics.\n- **Atomic per-method, transactional across composition.** Each `StockService` mutation (`receive` / `reserve` / `release` / `fulfill` / `transfer` / `adjust`) wraps every write in a single `db.transaction(...)` (via `@happyvertical/sql >= 0.74.0`). Partial failure rolls the whole call back — level writes and the matching `StockMovement` audit row commit together or not at all. `transfer` writes both legs (source decrement, destination increment, and both audit rows) inside one tx, so a failure mid-`transfer` is fully reverted.\n Callers composing multiple `StockService` calls into one logical unit (e.g. consuming materials line-by-line for a production order) should wrap them in `await stockService.withTransaction(async (tx) => { ... })` — `tx` is a tx-bound `StockService` with the same public API; mutation calls inside the callback share one transaction and either all commit or all roll back.\n If the underlying SQL adapter does not expose `transaction()` (extremely rare — all four built-in `@happyvertical/sql >= 0.74.0` adapters implement it), the service degrades to non-atomic serial writes and emits a one-time `console.warn`.\n- **Concurrent reservations: tightened, not bulletproof.** `@happyvertical/sql >= 0.74.0` serialises null-aware *upserts* via a per-key in-process lock (SQLite) or advisory lock (Postgres). That lock fixed the row-creation race (issue #1246) — two concurrent `create()` calls on the same conflict-column tuple no longer race the underlying `INSERT ... ON CONFLICT`. It does NOT serialise the read-modify-write compound that `reserve` / `fulfill` / `transfer` / `adjust` perform on an existing row: each method reads the current level (`SELECT`), computes the new value in JS, then writes it back (`UPDATE` via `save()` on the loaded row). The whole compound is inside one `db.transaction(...)` from round-3, so the level write and audit row commit together — but two concurrent transactions on the same `(skuId, locationId)` can each see the same starting balance, compute the same decrement, and double-spend. Awaited (serial) calls always behave correctly. Use a job queue (e.g. `@happyvertical/smrt-jobs`) or your own mutex when you need hard atomicity across concurrent callers. The full fix is `SELECT ... FOR UPDATE` inside the same transaction (Postgres) or upgrading the journal mode (SQLite); deferred until we see a real high-contention workload that warrants it.\n\n## Source attribution\n\nEvery `StockMovement` carries `sourceType` + `sourceId` so downstream queries can reconstruct \"what caused this movement\". Conventions used by the built-in dispatch handlers:\n\n| sourceType | Emitter | Note |\n|---|---|---|\n| `Contract` | `smrt-commerce` `contract:created` | All reservation/release/fulfilment legs caused by a contract |\n| `Fulfillment` | `smrt-commerce` `fulfillment:shipped` | Outbound shipment |\n| `PurchaseOrder` | (your code) | Inbound receipt |\n| `CycleCount` | (your code) | Adjustments from physical counts |\n| `TransferOrder` | (your code) | Both legs of a transfer |\n| `ProductionOrder` | `smrt-manufacturing` (issue #1250) | Materials consumed + finished goods produced |\n"
|
|
1394
|
+
"agentDoc": "# @happyvertical/smrt-inventory\n\nMulti-location stock tracking. Strictly industry-neutral — the same primitives serve apparel, furniture, automotive, CPG, electronics, and any other vertical that counts discrete units across locations.\n\n## Models\n\n| Model | Purpose |\n|---|---|\n| _(catalog shapes — `Product`, `Material`, `ProductVariant`, `Sku` — live in `@happyvertical/smrt-products`)_ | Sku is the smallest sellable / countable unit; its `attributes` JSON carries axis-value pins. ProductVariant is the per-product axis declaration. Import either from `@happyvertical/smrt-products` directly. |\n| `InventoryLocation` | Warehouse / factory / retail / in-transit / virtual. Open-ended `kind` string. Optional `placeId` references `@happyvertical/smrt-places`. `conflictColumns: ['code', 'tenant_id']`. |\n| `StockLevel` | Materialized `qty` for a `(skuId, locationId, state)` tuple. Upserted in place. **Mutated only by `StockService`.** States: `available`, `allocated`, `wip`, `qc_hold`, `damaged`. |\n| `StockMovement` | Append-only audit log. Every `StockService` mutation writes one (or two for `transfer`). `sourceType` + `sourceId` carry cross-package attribution. |\n\nAll three inventory models (`InventoryLocation`, `StockLevel`, `StockMovement`) use `@TenantScoped({ mode: 'optional' })` + nullable `tenantId` so they can be used either tenant-scoped or globally. The catalog shapes (`Product`, `Material`, `ProductVariant`, `Sku`) live in `@happyvertical/smrt-products` and carry their own tenant decoration there.\n\n## StockService — the only sanctioned way to mutate stock\n\n```typescript\nimport { createStockService } from '@happyvertical/smrt-inventory';\n\nconst service = await createStockService({ db });\nawait service.receive(skuId, locationId, qty, { sourceType, sourceId });\nawait service.reserve(skuId, locationId, qty, { sourceType, sourceId });\nawait service.release(skuId, locationId, qty, { sourceType, sourceId });\nawait service.fulfill(skuId, locationId, qty, { sourceType, sourceId });\nawait service.transfer(skuId, fromLocId, toLocId, qty, { sourceType, sourceId });\nawait service.adjust(skuId, locationId, delta, { sourceType, sourceId });\n```\n\n| Method | Behavior | Movement reason |\n|---|---|---|\n| `receive` | +qty available — purchase receipt, return, production produce | `receipt` |\n| `reserve` | available → allocated. Throws `InsufficientStockError` on overdraw | `reservation` |\n| `release` | allocated → available (order cancel) | `release` |\n| `fulfill` | -qty allocated. Stock leaves the building | `fulfillment` |\n| `transfer` | move stock between locations (writes two movements) | `transfer_out`, `transfer_in` |\n| `adjust` | signed delta — cycle counts and one-off corrections | `adjustment` |\n\nAll methods reject zero / negative / non-finite quantities except `adjust`, which accepts non-zero signed deltas. Negative deltas that would drive a state below zero throw `InsufficientStockError`.\n\n## Opt-in DispatchBus hooks\n\nOff by default. Wire them up explicitly in the application's `smrt.ts`:\n\n```typescript\nimport { installInventoryDispatchHandlers } from '@happyvertical/smrt-inventory';\n\nconst handlers = await installInventoryDispatchHandlers({\n dispatchBus: bus,\n db, // or stockService: existingService\n});\n```\n\nThis subscribes to:\n- `contract:created` → calls `service.reserve()` for every line inside one `stockService.withTransaction(...)`, attributed to `('Contract', payload.contractId)`\n- `fulfillment:shipped` → calls `service.fulfill()` for every line inside one `stockService.withTransaction(...)`, attributed to `('Fulfillment', payload.fulfillmentId)`\n\nBoth handlers are atomic across lines: a shortfall on line N rolls back lines 1..N-1 so the event is all-or-nothing. Without this guarantee, `DispatchBus` would only log the async handler error and a partially-reserved (or partially-fulfilled) contract would have no compensating event to fix it.\n\nPer-handler toggles: `installContractReserved`, `installFulfillmentShipped`. The `production_order:posted` hook is deliberately not installed here — it depends on the BOM model and ships in `@happyvertical/smrt-manufacturing` (issue #1250).\n\n## Schema migration (Phase 1 release)\n\nThe `Sku` model moved out of this package and into `@happyvertical/smrt-products`. Previously it lived in this package's `inventory_skus` table; it now lives in `product_skus` over there.\n\n**Upgrade procedure** for deployed consumers:\n\n1. **Let the framework create the destination table first.** Boot the new version once with `@happyvertical/smrt-products` registered in your `SmrtClassOptions`; the lazy `syncSchema` path will create `product_skus` with the right primary key, NOT NULL, UNIQUE (`code`, `tenant_id`), and indexes — they're derived from the `Sku` model decorators and would be stripped by a raw `CREATE TABLE AS SELECT`.\n\n2. **Idempotently copy rows across.** SQLite + Postgres:\n\n ```sql\n BEGIN;\n INSERT INTO product_skus (\n id, slug, context, created_at, updated_at,\n tenant_id, product_id, code, barcode, name,\n attributes, weight_grams, parent_sku_id, active\n )\n SELECT\n id, slug, context, created_at, updated_at,\n tenant_id, product_id, code, barcode, name,\n attributes, weight_grams, parent_sku_id, active\n FROM inventory_skus\n WHERE NOT EXISTS (\n SELECT 1 FROM product_skus p WHERE p.id = inventory_skus.id\n );\n COMMIT;\n ```\n\n3. **Drop the legacy table** once you've verified row counts match:\n\n ```sql\n DROP TABLE IF EXISTS inventory_skus;\n ```\n\n`StockLevel.skuId` and `StockMovement.skuId` carry plain string ids that still resolve to the same rows after the rename, so no inventory data has to move.\n\n## Gotchas\n\n- **Movements are append-only.** The materialized `StockLevel` row is derived state; the `StockMovement` ledger is the source of truth. Never let CRUD UIs expose update/delete on the movement table. If you find yourself wanting to \"fix\" a movement row, write a compensating `adjust()` call instead.\n- **Never call `StockLevel.create()` or `save()` directly.** Going around `StockService` silently desyncs the audit log and breaks cycle counts.\n- **Cross-industry constraint.** This package's vocabulary stays generic: `InventoryLocation`, `StockLevel`, `StockMovement`. Apparel-specific concepts (`Style`, `Makeup`, `Colorway`, fashion `Season`, tech-pack metadata) and their analogues in furniture / automotive / CPG live in the relevant template package, never here. PRs that introduce industry vocabulary should be rejected.\n- **Cross-package references are plain strings.** `skuId`, `placeId`, `sourceId` — all plain string ids, never `@foreignKey()`. Inventory's stock-motion logic only ever reads StockLevel/StockMovement; it doesn't follow `skuId` back to the catalog's Sku table, so the layering stays loose.\n- **`conflictColumns` include `tenant_id`** on `InventoryLocation` and `StockLevel`. NULL-matching semantics (`(code, NULL) IS NOT DISTINCT FROM (code, NULL)`) are handled by `@happyvertical/sql >= 0.74.0` natively, so two saves with the same `(code, NULL)` tuple correctly merge in place. Pass `nullsDistinct: true` at the sql layer to opt back into NULL-distinct semantics.\n- **Atomic per-method, transactional across composition.** Each `StockService` mutation (`receive` / `reserve` / `release` / `fulfill` / `transfer` / `adjust`) wraps every write in a single `db.transaction(...)` (via `@happyvertical/sql >= 0.74.0`). Partial failure rolls the whole call back — level writes and the matching `StockMovement` audit row commit together or not at all. `transfer` writes both legs (source decrement, destination increment, and both audit rows) inside one tx, so a failure mid-`transfer` is fully reverted.\n Callers composing multiple `StockService` calls into one logical unit (e.g. consuming materials line-by-line for a production order) should wrap them in `await stockService.withTransaction(async (tx) => { ... })` — `tx` is a tx-bound `StockService` with the same public API; mutation calls inside the callback share one transaction and either all commit or all roll back.\n If the underlying SQL adapter does not expose `transaction()` (extremely rare — all four built-in `@happyvertical/sql >= 0.74.0` adapters implement it), the service degrades to non-atomic serial writes and emits a one-time `console.warn`.\n- **Concurrent reservations: tightened, not bulletproof.** `@happyvertical/sql >= 0.74.0` serialises null-aware *upserts* via a per-key in-process lock (SQLite) or advisory lock (Postgres). That lock fixed the row-creation race (issue #1246) — two concurrent `create()` calls on the same conflict-column tuple no longer race the underlying `INSERT ... ON CONFLICT`. It does NOT serialise the read-modify-write compound that `reserve` / `fulfill` / `transfer` / `adjust` perform on an existing row: each method reads the current level (`SELECT`), computes the new value in JS, then writes it back (`UPDATE` via `save()` on the loaded row). The whole compound is inside one `db.transaction(...)` from round-3, so the level write and audit row commit together — but two concurrent transactions on the same `(skuId, locationId)` can each see the same starting balance, compute the same decrement, and double-spend. Awaited (serial) calls always behave correctly. Use a job queue (e.g. `@happyvertical/smrt-jobs`) or your own mutex when you need hard atomicity across concurrent callers. The full fix is `SELECT ... FOR UPDATE` inside the same transaction (Postgres) or upgrading the journal mode (SQLite); deferred until we see a real high-contention workload that warrants it.\n\n## Source attribution\n\nEvery `StockMovement` carries `sourceType` + `sourceId` so downstream queries can reconstruct \"what caused this movement\". Conventions used by the built-in dispatch handlers:\n\n| sourceType | Emitter | Note |\n|---|---|---|\n| `Contract` | `smrt-commerce` `contract:created` | All reservation/release/fulfilment legs caused by a contract |\n| `Fulfillment` | `smrt-commerce` `fulfillment:shipped` | Outbound shipment |\n| `PurchaseOrder` | (your code) | Inbound receipt |\n| `CycleCount` | (your code) | Adjustments from physical counts |\n| `TransferOrder` | (your code) | Both legs of a transfer |\n| `ProductionOrder` | `smrt-manufacturing` (issue #1250) | Materials consumed + finished goods produced |\n\n## Replenishment and browser components\n\n`StockService.setReorderPolicy()` is the sanctioned metadata writer for nullable\n`StockLevel.reorderPoint` / `reorderQuantity` on the available SKU/location row.\nIt does not change quantity or emit a movement. `findBelowReorderPoint()` uses\npublic tenant-filtered collection reads and strict less-than semantics.\n`StockMutationOptions.actorProfileId` propagates to every movement, including\nboth transfer legs; null retains compatibility for automation. It is a qualified\ncross-package profile reference, not an authorization decision.\n\n`src/svelte/` exports data-driven components through `./svelte`. Server callers\nown authentication and persistence callbacks; never import backend models into\nthis entry or expose ledger mutations as CRUD. Package `typecheck` includes\nSvelte checks and `test` includes DOM component tests. See README for the public\nprops, service contracts, and additive schema upgrade requirements.\n"
|
|
1310
1395
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Form, FormGroup, Input } from '@happyvertical/smrt-ui/forms';
|
|
3
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
4
|
+
import type { StockAdjustmentInput } from './types.js';
|
|
5
|
+
export interface Props {
|
|
6
|
+
skuId: string;
|
|
7
|
+
locationId: string;
|
|
8
|
+
onsubmit: (input: StockAdjustmentInput) => Promise<void> | void;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
const { skuId, locationId, onsubmit, disabled = false }: Props = $props();
|
|
12
|
+
let delta = $state<string | number>('');
|
|
13
|
+
let reasonCode = $state('adjustment');
|
|
14
|
+
let note = $state('');
|
|
15
|
+
let pending = $state(false);
|
|
16
|
+
let error = $state('');
|
|
17
|
+
let saved = $state(false);
|
|
18
|
+
async function submit(event: SubmitEvent) {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
if (pending || disabled) return;
|
|
21
|
+
error = '';
|
|
22
|
+
saved = false;
|
|
23
|
+
const quantity = Number(delta);
|
|
24
|
+
if (
|
|
25
|
+
!skuId.trim() ||
|
|
26
|
+
!locationId.trim() ||
|
|
27
|
+
!Number.isFinite(quantity) ||
|
|
28
|
+
quantity === 0 ||
|
|
29
|
+
!reasonCode.trim()
|
|
30
|
+
) {
|
|
31
|
+
error = 'Choose a SKU and location, a non-zero quantity, and a reason.';
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
pending = true;
|
|
35
|
+
try {
|
|
36
|
+
await onsubmit({
|
|
37
|
+
skuId,
|
|
38
|
+
locationId,
|
|
39
|
+
delta: quantity,
|
|
40
|
+
reasonCode: reasonCode.trim(),
|
|
41
|
+
note,
|
|
42
|
+
});
|
|
43
|
+
saved = true;
|
|
44
|
+
} catch (cause) {
|
|
45
|
+
error = cause instanceof Error ? cause.message : 'Unable to adjust stock.';
|
|
46
|
+
} finally {
|
|
47
|
+
pending = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
<Form onsubmit={submit} aria-label="Adjust stock">
|
|
52
|
+
<p>SKU: {skuId} · Location: {locationId}</p>
|
|
53
|
+
<FormGroup label="Quantity adjustment"><Input type="number" step="any" bind:value={delta} required disabled={disabled || pending} /></FormGroup>
|
|
54
|
+
<FormGroup label="Reason"><Input bind:value={reasonCode} required disabled={disabled || pending} /></FormGroup>
|
|
55
|
+
<FormGroup label="Note"><Input bind:value={note} disabled={disabled || pending} /></FormGroup>
|
|
56
|
+
{#if error}<p role="alert">{error}</p>{/if}
|
|
57
|
+
{#if saved}<p role="status">Stock adjusted</p>{/if}
|
|
58
|
+
<Button type="submit" disabled={disabled || pending}>{pending ? 'Saving…' : 'Adjust stock'}</Button>
|
|
59
|
+
</Form>
|