@open-mercato/core 0.10.1-develop.7709.1.046124599c → 0.10.1-develop.7714.1.9dbb8200d8
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/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/asn/index.js +19 -0
- package/dist/generated/entities/asn/index.js.map +7 -0
- package/dist/generated/entities/putaway_task/index.js +25 -0
- package/dist/generated/entities/putaway_task/index.js.map +7 -0
- package/dist/generated/entities/receiving_line/index.js +21 -0
- package/dist/generated/entities/receiving_line/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +4 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +34 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js +22 -3
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +2 -2
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +40 -11
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +13 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/wms/acl.js +2 -0
- package/dist/modules/wms/acl.js.map +2 -2
- package/dist/modules/wms/api/asns/[id]/complete/route.js +61 -0
- package/dist/modules/wms/api/asns/[id]/complete/route.js.map +7 -0
- package/dist/modules/wms/api/asns/[id]/receive/route.js +68 -0
- package/dist/modules/wms/api/asns/[id]/receive/route.js.map +7 -0
- package/dist/modules/wms/api/asns/route.js +197 -0
- package/dist/modules/wms/api/asns/route.js.map +7 -0
- package/dist/modules/wms/api/inventory/helpers.js +42 -34
- package/dist/modules/wms/api/inventory/helpers.js.map +2 -2
- package/dist/modules/wms/api/inventory/receive/route.js +1 -1
- package/dist/modules/wms/api/inventory/receive/route.js.map +2 -2
- package/dist/modules/wms/api/listEnrichers.js +23 -0
- package/dist/modules/wms/api/listEnrichers.js.map +2 -2
- package/dist/modules/wms/api/putaway-tasks/[id]/assign/route.js +56 -0
- package/dist/modules/wms/api/putaway-tasks/[id]/assign/route.js.map +7 -0
- package/dist/modules/wms/api/putaway-tasks/[id]/cancel/route.js +56 -0
- package/dist/modules/wms/api/putaway-tasks/[id]/cancel/route.js.map +7 -0
- package/dist/modules/wms/api/putaway-tasks/[id]/complete/route.js +65 -0
- package/dist/modules/wms/api/putaway-tasks/[id]/complete/route.js.map +7 -0
- package/dist/modules/wms/api/putaway-tasks/[id]/start/route.js +56 -0
- package/dist/modules/wms/api/putaway-tasks/[id]/start/route.js.map +7 -0
- package/dist/modules/wms/api/putaway-tasks/create-from-balance/route.js +51 -0
- package/dist/modules/wms/api/putaway-tasks/create-from-balance/route.js.map +7 -0
- package/dist/modules/wms/api/putaway-tasks/route.js +192 -0
- package/dist/modules/wms/api/putaway-tasks/route.js.map +7 -0
- package/dist/modules/wms/api/receiving-lines/route.js +180 -0
- package/dist/modules/wms/api/receiving-lines/route.js.map +7 -0
- package/dist/modules/wms/api/scan/putaway/route.js +104 -0
- package/dist/modules/wms/api/scan/putaway/route.js.map +7 -0
- package/dist/modules/wms/api/scan/receive/route.js +109 -0
- package/dist/modules/wms/api/scan/receive/route.js.map +7 -0
- package/dist/modules/wms/api/scan/resolve-location/route.js +89 -0
- package/dist/modules/wms/api/scan/resolve-location/route.js.map +7 -0
- package/dist/modules/wms/api/scan/resolve-lot/route.js +89 -0
- package/dist/modules/wms/api/scan/resolve-lot/route.js.map +7 -0
- package/dist/modules/wms/backend/wms/asns/[id]/page.js +9 -0
- package/dist/modules/wms/backend/wms/asns/[id]/page.js.map +7 -0
- package/dist/modules/wms/backend/wms/asns/[id]/page.meta.js +18 -0
- package/dist/modules/wms/backend/wms/asns/[id]/page.meta.js.map +7 -0
- package/dist/modules/wms/backend/wms/asns/page.js +9 -0
- package/dist/modules/wms/backend/wms/asns/page.js.map +7 -0
- package/dist/modules/wms/backend/wms/asns/page.meta.js +18 -0
- package/dist/modules/wms/backend/wms/asns/page.meta.js.map +7 -0
- package/dist/modules/wms/backend/wms/putaway/page.js +9 -0
- package/dist/modules/wms/backend/wms/putaway/page.js.map +7 -0
- package/dist/modules/wms/backend/wms/putaway/page.meta.js +18 -0
- package/dist/modules/wms/backend/wms/putaway/page.meta.js.map +7 -0
- package/dist/modules/wms/backend/wms/receiving/page.js +8 -0
- package/dist/modules/wms/backend/wms/receiving/page.js.map +7 -0
- package/dist/modules/wms/backend/wms/receiving/page.meta.js +18 -0
- package/dist/modules/wms/backend/wms/receiving/page.meta.js.map +7 -0
- package/dist/modules/wms/ce.js +27 -0
- package/dist/modules/wms/ce.js.map +2 -2
- package/dist/modules/wms/commands/asn.js +1746 -0
- package/dist/modules/wms/commands/asn.js.map +7 -0
- package/dist/modules/wms/commands/index.js +2 -0
- package/dist/modules/wms/commands/index.js.map +2 -2
- package/dist/modules/wms/commands/inventory-actions.js +347 -243
- package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
- package/dist/modules/wms/commands/putaway.js +945 -0
- package/dist/modules/wms/commands/putaway.js.map +7 -0
- package/dist/modules/wms/commands/shared.js +51 -0
- package/dist/modules/wms/commands/shared.js.map +2 -2
- package/dist/modules/wms/components/backend/AssignPutawayTaskDialog.js +278 -0
- package/dist/modules/wms/components/backend/AssignPutawayTaskDialog.js.map +7 -0
- package/dist/modules/wms/components/backend/CompletePutawayTaskDialog.js +327 -0
- package/dist/modules/wms/components/backend/CompletePutawayTaskDialog.js.map +7 -0
- package/dist/modules/wms/components/backend/CreateAsnDialog.js +404 -0
- package/dist/modules/wms/components/backend/CreateAsnDialog.js.map +7 -0
- package/dist/modules/wms/components/backend/InventoryOperationsSection.js +16 -3
- package/dist/modules/wms/components/backend/InventoryOperationsSection.js.map +2 -2
- package/dist/modules/wms/components/backend/ReceiveAsnLineDialog.js +476 -0
- package/dist/modules/wms/components/backend/ReceiveAsnLineDialog.js.map +7 -0
- package/dist/modules/wms/components/backend/ReceiveInventoryDialog.js +2 -2
- package/dist/modules/wms/components/backend/ReceiveInventoryDialog.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsAsnDetailPage.js +524 -0
- package/dist/modules/wms/components/backend/WmsAsnDetailPage.js.map +7 -0
- package/dist/modules/wms/components/backend/WmsAsnsListPage.js +339 -0
- package/dist/modules/wms/components/backend/WmsAsnsListPage.js.map +7 -0
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +5 -6
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js +5 -6
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js +14 -10
- package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsPutawayQueuePage.js +598 -0
- package/dist/modules/wms/components/backend/WmsPutawayQueuePage.js.map +7 -0
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +5 -6
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/asnCompleteOptimisticLock.js +32 -0
- package/dist/modules/wms/components/backend/asnCompleteOptimisticLock.js.map +7 -0
- package/dist/modules/wms/components/backend/asnReceivingLinesLoader.js +36 -0
- package/dist/modules/wms/components/backend/asnReceivingLinesLoader.js.map +7 -0
- package/dist/modules/wms/components/backend/inboundStatusUi.js +106 -0
- package/dist/modules/wms/components/backend/inboundStatusUi.js.map +7 -0
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +17 -0
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
- package/dist/modules/wms/components/backend/putawayQueueOptimisticLock.js +14 -0
- package/dist/modules/wms/components/backend/putawayQueueOptimisticLock.js.map +7 -0
- package/dist/modules/wms/components/backend/useWmsInventoryMutationAccess.js +4 -0
- package/dist/modules/wms/components/backend/useWmsInventoryMutationAccess.js.map +2 -2
- package/dist/modules/wms/data/enrichers.js +117 -12
- package/dist/modules/wms/data/enrichers.js.map +2 -2
- package/dist/modules/wms/data/entities.js +150 -1
- package/dist/modules/wms/data/entities.js.map +2 -2
- package/dist/modules/wms/data/validators.js +214 -0
- package/dist/modules/wms/data/validators.js.map +2 -2
- package/dist/modules/wms/events.js +12 -1
- package/dist/modules/wms/events.js.map +2 -2
- package/dist/modules/wms/extension-points.js +3 -1
- package/dist/modules/wms/extension-points.js.map +2 -2
- package/dist/modules/wms/lib/asnReceiving.js +124 -0
- package/dist/modules/wms/lib/asnReceiving.js.map +7 -0
- package/dist/modules/wms/lib/asnVendor.js +52 -0
- package/dist/modules/wms/lib/asnVendor.js.map +7 -0
- package/dist/modules/wms/lib/flashMutationError.js +4 -0
- package/dist/modules/wms/lib/flashMutationError.js.map +2 -2
- package/dist/modules/wms/lib/inventoryDisplayUi.js +24 -0
- package/dist/modules/wms/lib/inventoryDisplayUi.js.map +2 -2
- package/dist/modules/wms/lib/inventoryPolicy.js +17 -0
- package/dist/modules/wms/lib/inventoryPolicy.js.map +2 -2
- package/dist/modules/wms/lib/loadOperationalDashboard.js +67 -1
- package/dist/modules/wms/lib/loadOperationalDashboard.js.map +2 -2
- package/dist/modules/wms/lib/putaway.js +141 -0
- package/dist/modules/wms/lib/putaway.js.map +7 -0
- package/dist/modules/wms/lib/roleFeatures.js +3 -1
- package/dist/modules/wms/lib/roleFeatures.js.map +2 -2
- package/dist/modules/wms/lib/salesOrderInventoryAutomation.js +99 -1
- package/dist/modules/wms/lib/salesOrderInventoryAutomation.js.map +2 -2
- package/dist/modules/wms/lib/scanResolve.js +147 -0
- package/dist/modules/wms/lib/scanResolve.js.map +7 -0
- package/dist/modules/wms/lib/wmsIntegrationToggles.js +1 -1
- package/dist/modules/wms/lib/wmsIntegrationToggles.js.map +2 -2
- package/dist/modules/wms/migrations/Migration20260812120000.js +43 -0
- package/dist/modules/wms/migrations/Migration20260812120000.js.map +7 -0
- package/dist/modules/wms/migrations/Migration20260812130000.js +30 -0
- package/dist/modules/wms/migrations/Migration20260812130000.js.map +7 -0
- package/dist/modules/wms/migrations/Migration20260822120000.js +17 -0
- package/dist/modules/wms/migrations/Migration20260822120000.js.map +7 -0
- package/dist/modules/wms/migrations/Migration20260822130000.js +17 -0
- package/dist/modules/wms/migrations/Migration20260822130000.js.map +7 -0
- package/dist/modules/wms/migrations/Migration20261029120000.js +13 -0
- package/dist/modules/wms/migrations/Migration20261029120000.js.map +7 -0
- package/dist/modules/wms/search.js +85 -0
- package/dist/modules/wms/search.js.map +2 -2
- package/dist/modules/wms/subscribers/asn-line-received-reevaluate-reservations.js +14 -0
- package/dist/modules/wms/subscribers/asn-line-received-reevaluate-reservations.js.map +7 -0
- package/dist/modules/wms/subscribers/invalidate-enricher-cache-asn.js +12 -0
- package/dist/modules/wms/subscribers/invalidate-enricher-cache-asn.js.map +7 -0
- package/dist/modules/wms/subscribers/procurement-goods-receipt-created.js +163 -0
- package/dist/modules/wms/subscribers/procurement-goods-receipt-created.js.map +7 -0
- package/dist/modules/wms/subscribers/putaway-completed-reevaluate-reservations.js +14 -0
- package/dist/modules/wms/subscribers/putaway-completed-reevaluate-reservations.js.map +7 -0
- package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.client.js +37 -8
- package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.client.js.map +2 -2
- package/generated/entities/asn/index.ts +8 -0
- package/generated/entities/putaway_task/index.ts +11 -0
- package/generated/entities/receiving_line/index.ts +9 -0
- package/generated/entities.ids.generated.ts +4 -1
- package/generated/entity-fields-registry.ts +34 -0
- package/package.json +7 -7
- package/src/modules/auth/i18n/de.json +2 -0
- package/src/modules/auth/i18n/en.json +2 -0
- package/src/modules/auth/i18n/es.json +2 -0
- package/src/modules/auth/i18n/ko.json +2 -0
- package/src/modules/auth/i18n/pl.json +2 -0
- package/src/modules/communication_channels/i18n/de.json +2 -0
- package/src/modules/communication_channels/i18n/en.json +2 -0
- package/src/modules/communication_channels/i18n/es.json +2 -0
- package/src/modules/communication_channels/i18n/ko.json +2 -0
- package/src/modules/communication_channels/i18n/pl.json +2 -0
- package/src/modules/communication_channels/lib/sanitize-channel-html.ts +23 -3
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +44 -12
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +16 -2
- package/src/modules/wms/acl.ts +2 -0
- package/src/modules/wms/api/asns/[id]/complete/route.ts +62 -0
- package/src/modules/wms/api/asns/[id]/receive/route.ts +74 -0
- package/src/modules/wms/api/asns/route.ts +200 -0
- package/src/modules/wms/api/inventory/helpers.ts +57 -34
- package/src/modules/wms/api/inventory/receive/route.ts +2 -1
- package/src/modules/wms/api/listEnrichers.ts +38 -0
- package/src/modules/wms/api/putaway-tasks/[id]/assign/route.ts +56 -0
- package/src/modules/wms/api/putaway-tasks/[id]/cancel/route.ts +56 -0
- package/src/modules/wms/api/putaway-tasks/[id]/complete/route.ts +66 -0
- package/src/modules/wms/api/putaway-tasks/[id]/start/route.ts +56 -0
- package/src/modules/wms/api/putaway-tasks/create-from-balance/route.ts +50 -0
- package/src/modules/wms/api/putaway-tasks/route.ts +199 -0
- package/src/modules/wms/api/receiving-lines/route.ts +180 -0
- package/src/modules/wms/api/scan/putaway/route.ts +108 -0
- package/src/modules/wms/api/scan/receive/route.ts +118 -0
- package/src/modules/wms/api/scan/resolve-location/route.ts +92 -0
- package/src/modules/wms/api/scan/resolve-lot/route.ts +92 -0
- package/src/modules/wms/backend/wms/asns/[id]/page.meta.ts +14 -0
- package/src/modules/wms/backend/wms/asns/[id]/page.tsx +5 -0
- package/src/modules/wms/backend/wms/asns/page.meta.ts +14 -0
- package/src/modules/wms/backend/wms/asns/page.tsx +5 -0
- package/src/modules/wms/backend/wms/putaway/page.meta.ts +14 -0
- package/src/modules/wms/backend/wms/putaway/page.tsx +5 -0
- package/src/modules/wms/backend/wms/receiving/page.meta.ts +14 -0
- package/src/modules/wms/backend/wms/receiving/page.tsx +5 -0
- package/src/modules/wms/ce.ts +27 -0
- package/src/modules/wms/commands/asn.ts +2122 -0
- package/src/modules/wms/commands/index.ts +2 -0
- package/src/modules/wms/commands/inventory-actions.ts +413 -243
- package/src/modules/wms/commands/putaway.ts +1169 -0
- package/src/modules/wms/commands/shared.ts +51 -0
- package/src/modules/wms/components/backend/AssignPutawayTaskDialog.tsx +328 -0
- package/src/modules/wms/components/backend/CompletePutawayTaskDialog.tsx +366 -0
- package/src/modules/wms/components/backend/CreateAsnDialog.tsx +427 -0
- package/src/modules/wms/components/backend/InventoryOperationsSection.tsx +20 -2
- package/src/modules/wms/components/backend/ReceiveAsnLineDialog.tsx +535 -0
- package/src/modules/wms/components/backend/ReceiveInventoryDialog.tsx +2 -2
- package/src/modules/wms/components/backend/WmsAsnDetailPage.tsx +647 -0
- package/src/modules/wms/components/backend/WmsAsnsListPage.tsx +407 -0
- package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +4 -5
- package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +4 -5
- package/src/modules/wms/components/backend/WmsOperationalDashboardPage.tsx +12 -8
- package/src/modules/wms/components/backend/WmsPutawayQueuePage.tsx +677 -0
- package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +4 -5
- package/src/modules/wms/components/backend/asnCompleteOptimisticLock.ts +52 -0
- package/src/modules/wms/components/backend/asnReceivingLinesLoader.ts +83 -0
- package/src/modules/wms/components/backend/inboundStatusUi.ts +154 -0
- package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +20 -0
- package/src/modules/wms/components/backend/putawayQueueOptimisticLock.ts +20 -0
- package/src/modules/wms/components/backend/useWmsInventoryMutationAccess.ts +4 -0
- package/src/modules/wms/data/enrichers.ts +175 -13
- package/src/modules/wms/data/entities.ts +155 -0
- package/src/modules/wms/data/validators.ts +256 -0
- package/src/modules/wms/events.ts +11 -0
- package/src/modules/wms/extension-points.ts +2 -0
- package/src/modules/wms/i18n/de.json +205 -3
- package/src/modules/wms/i18n/en.json +205 -3
- package/src/modules/wms/i18n/es.json +205 -3
- package/src/modules/wms/i18n/ko.json +205 -3
- package/src/modules/wms/i18n/pl.json +205 -3
- package/src/modules/wms/lib/asnReceiving.ts +226 -0
- package/src/modules/wms/lib/asnVendor.ts +80 -0
- package/src/modules/wms/lib/flashMutationError.ts +4 -0
- package/src/modules/wms/lib/inventoryDisplayUi.ts +46 -0
- package/src/modules/wms/lib/inventoryPolicy.ts +31 -1
- package/src/modules/wms/lib/loadOperationalDashboard.ts +89 -1
- package/src/modules/wms/lib/putaway.ts +210 -0
- package/src/modules/wms/lib/roleFeatures.ts +2 -0
- package/src/modules/wms/lib/salesOrderInventoryAutomation.ts +150 -1
- package/src/modules/wms/lib/scanResolve.ts +185 -0
- package/src/modules/wms/lib/wmsIntegrationToggles.ts +2 -1
- package/src/modules/wms/migrations/.snapshot-open-mercato.json +1000 -0
- package/src/modules/wms/migrations/Migration20260812120000.ts +43 -0
- package/src/modules/wms/migrations/Migration20260812130000.ts +28 -0
- package/src/modules/wms/migrations/Migration20260822120000.ts +17 -0
- package/src/modules/wms/migrations/Migration20260822130000.ts +15 -0
- package/src/modules/wms/migrations/Migration20261029120000.ts +11 -0
- package/src/modules/wms/search.ts +95 -0
- package/src/modules/wms/subscribers/asn-line-received-reevaluate-reservations.ts +21 -0
- package/src/modules/wms/subscribers/invalidate-enricher-cache-asn.ts +12 -0
- package/src/modules/wms/subscribers/procurement-goods-receipt-created.ts +238 -0
- package/src/modules/wms/subscribers/putaway-completed-reevaluate-reservations.ts +21 -0
- package/src/modules/wms/widgets/injection/sales-order-stock-context/widget.client.tsx +63 -13
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations'
|
|
2
|
+
|
|
3
|
+
export class Migration20260812120000_wms extends Migration {
|
|
4
|
+
override up(): void | Promise<void> {
|
|
5
|
+
this.addSql(
|
|
6
|
+
`create table "wms_asns" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "metadata" jsonb null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, "warehouse_id" uuid not null, "vendor_id" uuid null, "status" text not null default 'draft', "expected_at" timestamptz not null, "reference_number" text null, "notes" text null, primary key ("id"));`,
|
|
7
|
+
)
|
|
8
|
+
this.addSql(
|
|
9
|
+
`create index "wms_asns_org_warehouse_status_expected_idx" on "wms_asns" ("organization_id", "warehouse_id", "status", "expected_at");`,
|
|
10
|
+
)
|
|
11
|
+
this.addSql(
|
|
12
|
+
`create index "wms_asns_org_vendor_expected_idx" on "wms_asns" ("organization_id", "vendor_id", "expected_at" desc) where deleted_at is null;`,
|
|
13
|
+
)
|
|
14
|
+
this.addSql(`create index "wms_asns_org_tenant_idx" on "wms_asns" ("organization_id", "tenant_id");`)
|
|
15
|
+
|
|
16
|
+
this.addSql(
|
|
17
|
+
`create table "wms_receiving_lines" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "metadata" jsonb null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, "asn_id" uuid not null, "catalog_variant_id" uuid not null, "expected_qty" numeric(16,4) not null, "received_qty" numeric(16,4) not null default '0', "lot_number" text null, "serial_numbers" jsonb null, "qc_status" text not null default 'pending', "target_staging_location_id" uuid null, "rejection_reason" text null, primary key ("id"));`,
|
|
18
|
+
)
|
|
19
|
+
this.addSql(
|
|
20
|
+
`create index "wms_receiving_lines_org_variant_qc_idx" on "wms_receiving_lines" ("organization_id", "catalog_variant_id", "qc_status");`,
|
|
21
|
+
)
|
|
22
|
+
this.addSql(
|
|
23
|
+
`create index "wms_receiving_lines_org_asn_idx" on "wms_receiving_lines" ("organization_id", "asn_id");`,
|
|
24
|
+
)
|
|
25
|
+
this.addSql(
|
|
26
|
+
`create index "wms_receiving_lines_org_tenant_idx" on "wms_receiving_lines" ("organization_id", "tenant_id");`,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
this.addSql(
|
|
30
|
+
`alter table "wms_asns" add constraint "wms_asns_warehouse_id_foreign" foreign key ("warehouse_id") references "wms_warehouses" ("id");`,
|
|
31
|
+
)
|
|
32
|
+
this.addSql(
|
|
33
|
+
`alter table "wms_receiving_lines" add constraint "wms_receiving_lines_asn_id_foreign" foreign key ("asn_id") references "wms_asns" ("id");`,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override down(): void | Promise<void> {
|
|
38
|
+
this.addSql(`alter table "wms_receiving_lines" drop constraint if exists "wms_receiving_lines_asn_id_foreign";`)
|
|
39
|
+
this.addSql(`alter table "wms_asns" drop constraint if exists "wms_asns_warehouse_id_foreign";`)
|
|
40
|
+
this.addSql(`drop table if exists "wms_receiving_lines" cascade;`)
|
|
41
|
+
this.addSql(`drop table if exists "wms_asns" cascade;`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations'
|
|
2
|
+
|
|
3
|
+
export class Migration20260812130000_wms extends Migration {
|
|
4
|
+
override up(): void | Promise<void> {
|
|
5
|
+
this.addSql(
|
|
6
|
+
`create table "wms_putaway_tasks" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "metadata" jsonb null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, "warehouse_id" uuid not null, "source_location_id" uuid not null, "target_location_id" uuid null, "catalog_variant_id" uuid not null, "lot_id" uuid null, "quantity" numeric(16,4) not null, "status" text not null default 'open', "assigned_to" uuid null, "priority" int not null default 5, primary key ("id"));`,
|
|
7
|
+
)
|
|
8
|
+
this.addSql(
|
|
9
|
+
`create index "wms_putaway_tasks_org_warehouse_status_priority_idx" on "wms_putaway_tasks" ("organization_id", "warehouse_id", "status", "priority");`,
|
|
10
|
+
)
|
|
11
|
+
this.addSql(
|
|
12
|
+
`create index "wms_putaway_tasks_org_assigned_status_idx" on "wms_putaway_tasks" ("organization_id", "assigned_to", "status");`,
|
|
13
|
+
)
|
|
14
|
+
this.addSql(
|
|
15
|
+
`create index "wms_putaway_tasks_org_tenant_idx" on "wms_putaway_tasks" ("organization_id", "tenant_id");`,
|
|
16
|
+
)
|
|
17
|
+
this.addSql(
|
|
18
|
+
`alter table "wms_putaway_tasks" add constraint "wms_putaway_tasks_warehouse_id_foreign" foreign key ("warehouse_id") references "wms_warehouses" ("id");`,
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
override down(): void | Promise<void> {
|
|
23
|
+
this.addSql(
|
|
24
|
+
`alter table "wms_putaway_tasks" drop constraint if exists "wms_putaway_tasks_warehouse_id_foreign";`,
|
|
25
|
+
)
|
|
26
|
+
this.addSql(`drop table if exists "wms_putaway_tasks" cascade;`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations'
|
|
2
|
+
|
|
3
|
+
export class Migration20260822120000_wms extends Migration {
|
|
4
|
+
override up(): void | Promise<void> {
|
|
5
|
+
this.addSql(`alter table "wms_putaway_tasks" add column "putaway_key" text null;`)
|
|
6
|
+
// Exclude cancelled so cancel-without-clearing-key (legacy rows) cannot block
|
|
7
|
+
// ASN receive recreate; cancel also nulls putaway_key in the command path.
|
|
8
|
+
this.addSql(
|
|
9
|
+
`create unique index "wms_putaway_tasks_org_putaway_key_unique_idx" on "wms_putaway_tasks" ("organization_id", "putaway_key") where putaway_key is not null and deleted_at is null and status <> 'cancelled';`,
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
override down(): void | Promise<void> {
|
|
14
|
+
this.addSql(`drop index if exists "wms_putaway_tasks_org_putaway_key_unique_idx";`)
|
|
15
|
+
this.addSql(`alter table "wms_putaway_tasks" drop column if exists "putaway_key";`)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations'
|
|
2
|
+
|
|
3
|
+
export class Migration20260822130000_wms extends Migration {
|
|
4
|
+
override up(): void | Promise<void> {
|
|
5
|
+
this.addSql(`alter table "wms_asns" add column "source_key" text null;`)
|
|
6
|
+
this.addSql(
|
|
7
|
+
`create unique index "wms_asns_org_source_key_unique_idx" on "wms_asns" ("organization_id", "source_key") where source_key is not null and deleted_at is null;`,
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
override down(): void | Promise<void> {
|
|
12
|
+
this.addSql(`drop index if exists "wms_asns_org_source_key_unique_idx";`)
|
|
13
|
+
this.addSql(`alter table "wms_asns" drop column if exists "source_key";`)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations'
|
|
2
|
+
|
|
3
|
+
export class Migration20261029120000_wms extends Migration {
|
|
4
|
+
override up(): void | Promise<void> {
|
|
5
|
+
this.addSql(`alter table "wms_putaway_tasks" add column "serial_number" text null;`)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
override down(): void | Promise<void> {
|
|
9
|
+
this.addSql(`alter table "wms_putaway_tasks" drop column if exists "serial_number";`)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -102,6 +102,37 @@ function buildLotPresenter(
|
|
|
102
102
|
return { title, subtitle, icon: 'package-search' }
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
function buildAsnPresenter(
|
|
106
|
+
t: TranslateFn,
|
|
107
|
+
record: Record<string, unknown>,
|
|
108
|
+
): SearchResultPresenter {
|
|
109
|
+
const label = t('wms.search.badge.asn', 'ASN')
|
|
110
|
+
const title =
|
|
111
|
+
pickString(record.reference_number, record.referenceNumber, record.id) ?? label
|
|
112
|
+
const subtitle = formatSubtitle(
|
|
113
|
+
record.status,
|
|
114
|
+
record.warehouse_id ?? record.warehouseId,
|
|
115
|
+
record.expected_at ?? record.expectedAt,
|
|
116
|
+
)
|
|
117
|
+
return { title, subtitle, icon: 'truck', badge: label }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function buildPutawayPresenter(
|
|
121
|
+
t: TranslateFn,
|
|
122
|
+
record: Record<string, unknown>,
|
|
123
|
+
): SearchResultPresenter {
|
|
124
|
+
const label = t('wms.search.badge.putaway', 'Putaway task')
|
|
125
|
+
const title =
|
|
126
|
+
pickString(record.catalog_variant_id, record.catalogVariantId, record.id) ?? label
|
|
127
|
+
const subtitle = formatSubtitle(
|
|
128
|
+
record.status,
|
|
129
|
+
record.warehouse_id ?? record.warehouseId,
|
|
130
|
+
record.source_location_id ?? record.sourceLocationId,
|
|
131
|
+
record.target_location_id ?? record.targetLocationId,
|
|
132
|
+
)
|
|
133
|
+
return { title, subtitle, icon: 'package', badge: label }
|
|
134
|
+
}
|
|
135
|
+
|
|
105
136
|
export const searchConfig: SearchModuleConfig = {
|
|
106
137
|
entities: [
|
|
107
138
|
{
|
|
@@ -227,6 +258,70 @@ export const searchConfig: SearchModuleConfig = {
|
|
|
227
258
|
searchable: ['lot_number', 'batch_number', 'sku', 'catalog_variant_id', 'status', 'expires_at'],
|
|
228
259
|
},
|
|
229
260
|
},
|
|
261
|
+
{
|
|
262
|
+
entityId: E.wms.asn,
|
|
263
|
+
aclFeatures: ['wms.view'],
|
|
264
|
+
enabled: true,
|
|
265
|
+
priority: 7,
|
|
266
|
+
buildSource: async (ctx) => {
|
|
267
|
+
const { t } = await resolveTranslations()
|
|
268
|
+
const record = ctx.record
|
|
269
|
+
const lines: string[] = []
|
|
270
|
+
appendLine(lines, 'Reference', record.reference_number ?? record.referenceNumber)
|
|
271
|
+
appendLine(lines, 'Status', record.status)
|
|
272
|
+
appendLine(lines, 'Warehouse', record.warehouse_id ?? record.warehouseId)
|
|
273
|
+
appendLine(lines, 'Vendor', record.vendor_id ?? record.vendorId)
|
|
274
|
+
appendLine(lines, 'Expected at', record.expected_at ?? record.expectedAt)
|
|
275
|
+
appendLine(lines, 'Notes', record.notes)
|
|
276
|
+
return buildIndexSource(ctx, buildAsnPresenter(t, record), lines)
|
|
277
|
+
},
|
|
278
|
+
formatResult: async (ctx) => {
|
|
279
|
+
const { t } = await resolveTranslations()
|
|
280
|
+
return buildAsnPresenter(t, ctx.record)
|
|
281
|
+
},
|
|
282
|
+
resolveUrl: async (ctx) => {
|
|
283
|
+
const id = pickString(ctx.record.id)
|
|
284
|
+
if (id) return `${WMS_ROOT_URL}/asns/${id}`
|
|
285
|
+
return WMS_ROOT_URL
|
|
286
|
+
},
|
|
287
|
+
fieldPolicy: {
|
|
288
|
+
searchable: ['reference_number', 'status', 'warehouse_id', 'vendor_id', 'notes'],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
entityId: E.wms.putaway_task,
|
|
293
|
+
aclFeatures: ['wms.view'],
|
|
294
|
+
enabled: true,
|
|
295
|
+
priority: 6,
|
|
296
|
+
buildSource: async (ctx) => {
|
|
297
|
+
const { t } = await resolveTranslations()
|
|
298
|
+
const record = ctx.record
|
|
299
|
+
const lines: string[] = []
|
|
300
|
+
appendLine(lines, 'Status', record.status)
|
|
301
|
+
appendLine(lines, 'Warehouse', record.warehouse_id ?? record.warehouseId)
|
|
302
|
+
appendLine(lines, 'Source', record.source_location_id ?? record.sourceLocationId)
|
|
303
|
+
appendLine(lines, 'Target', record.target_location_id ?? record.targetLocationId)
|
|
304
|
+
appendLine(lines, 'Variant', record.catalog_variant_id ?? record.catalogVariantId)
|
|
305
|
+
appendLine(lines, 'Assigned to', record.assigned_to ?? record.assignedTo)
|
|
306
|
+
appendLine(lines, 'Quantity', record.quantity)
|
|
307
|
+
return buildIndexSource(ctx, buildPutawayPresenter(t, record), lines)
|
|
308
|
+
},
|
|
309
|
+
formatResult: async (ctx) => {
|
|
310
|
+
const { t } = await resolveTranslations()
|
|
311
|
+
return buildPutawayPresenter(t, ctx.record)
|
|
312
|
+
},
|
|
313
|
+
resolveUrl: async () => `${WMS_ROOT_URL}/putaway`,
|
|
314
|
+
fieldPolicy: {
|
|
315
|
+
searchable: [
|
|
316
|
+
'status',
|
|
317
|
+
'warehouse_id',
|
|
318
|
+
'source_location_id',
|
|
319
|
+
'target_location_id',
|
|
320
|
+
'catalog_variant_id',
|
|
321
|
+
'assigned_to',
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
},
|
|
230
325
|
],
|
|
231
326
|
}
|
|
232
327
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { reevaluateReservationsAfterStockIncrease } from '../lib/salesOrderInventoryAutomation'
|
|
2
|
+
|
|
3
|
+
export const metadata = {
|
|
4
|
+
event: 'wms.asn.line_received',
|
|
5
|
+
persistent: true,
|
|
6
|
+
id: 'wms:asn-line-received-reevaluate-reservations',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type SubscriberContext = {
|
|
10
|
+
resolve: <T = unknown>(name: string) => T
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type AsnLineReceivedPayload = {
|
|
14
|
+
catalogVariantId?: string | null
|
|
15
|
+
tenantId?: string | null
|
|
16
|
+
organizationId?: string | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default async function handle(payload: AsnLineReceivedPayload, ctx: SubscriberContext) {
|
|
20
|
+
await reevaluateReservationsAfterStockIncrease(payload, ctx)
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
|
|
2
|
+
|
|
3
|
+
// Sales-order enricher `inboundSummary` aggregates open ASN rows (`draft` /
|
|
4
|
+
// `in_transit`). ASN lifecycle writes change that projection without always
|
|
5
|
+
// touching balances, so they need their own invalidation family.
|
|
6
|
+
export const metadata = {
|
|
7
|
+
event: 'wms.asn.*',
|
|
8
|
+
persistent: false,
|
|
9
|
+
id: 'wms:invalidate-enricher-cache-asn',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default createInventoryEnricherCacheHandler('inventory')
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
|
|
3
|
+
import type { FeatureTogglesService } from '@open-mercato/core/modules/feature_toggles/lib/feature-flag-check'
|
|
4
|
+
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
5
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
6
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
7
|
+
import { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'
|
|
8
|
+
import { Asn } from '../data/entities'
|
|
9
|
+
import { resolveWmsIntegrationToggleEnabled } from '../lib/wmsIntegrationToggles'
|
|
10
|
+
|
|
11
|
+
const logger = createLogger('wms')
|
|
12
|
+
|
|
13
|
+
export const metadata = {
|
|
14
|
+
event: 'procurement.goods_receipt.created',
|
|
15
|
+
persistent: true,
|
|
16
|
+
id: 'wms:procurement-goods-receipt-created',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type SubscriberContext = {
|
|
20
|
+
resolve: <T = unknown>(name: string) => T
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Assumed procurement.goods_receipt.created payload (procurement module is
|
|
25
|
+
* optional / thin today). Fields beyond id + scope are best-effort:
|
|
26
|
+
*
|
|
27
|
+
* - id / goodsReceiptId: required for idempotent create via Asn.sourceKey
|
|
28
|
+
* - warehouseId (required to create): destination warehouse UUID
|
|
29
|
+
* - vendorId?: customers UUID
|
|
30
|
+
* - expectedAt?: ISO date (defaults to now)
|
|
31
|
+
* - referenceNumber?: external PO/GRN display reference (falls back to goods receipt id;
|
|
32
|
+
* not used as the uniqueness key — free-form refs may legitimately collide)
|
|
33
|
+
* - tenantId / organizationId: required scope
|
|
34
|
+
* - lines?: [{ catalogVariantId, expectedQty, lotNumber? }]
|
|
35
|
+
*
|
|
36
|
+
* When the toggle is off, required fields are missing, or peers are absent,
|
|
37
|
+
* this subscriber no-ops.
|
|
38
|
+
*/
|
|
39
|
+
type GoodsReceiptCreatedPayload = {
|
|
40
|
+
id?: string | null
|
|
41
|
+
goodsReceiptId?: string | null
|
|
42
|
+
warehouseId?: string | null
|
|
43
|
+
vendorId?: string | null
|
|
44
|
+
expectedAt?: string | Date | null
|
|
45
|
+
referenceNumber?: string | null
|
|
46
|
+
tenantId?: string | null
|
|
47
|
+
organizationId?: string | null
|
|
48
|
+
lines?: Array<{
|
|
49
|
+
catalogVariantId?: string | null
|
|
50
|
+
expectedQty?: string | number | null
|
|
51
|
+
lotNumber?: string | null
|
|
52
|
+
}> | null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function buildProcurementGoodsReceiptSourceKey(goodsReceiptId: string): string {
|
|
56
|
+
return `procurement.goods_receipt:${goodsReceiptId}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function tryResolve<T>(ctx: SubscriberContext, name: string): T | undefined {
|
|
60
|
+
try {
|
|
61
|
+
return ctx.resolve<T>(name)
|
|
62
|
+
} catch {
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function asUuid(value: unknown): string | null {
|
|
68
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function toPositiveQuantity(value: unknown): number | null {
|
|
72
|
+
const numeric = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN
|
|
73
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return null
|
|
74
|
+
return numeric
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isUniqueConstraintError(error: unknown): boolean {
|
|
78
|
+
if (!error || typeof error !== 'object') return false
|
|
79
|
+
const code = (error as { code?: string }).code
|
|
80
|
+
if (code === '23505') return true
|
|
81
|
+
const name = (error as { name?: string }).name
|
|
82
|
+
if (name === 'UniqueConstraintViolationException') return true
|
|
83
|
+
const cause = (error as { cause?: unknown }).cause
|
|
84
|
+
if (cause && cause !== error) return isUniqueConstraintError(cause)
|
|
85
|
+
const driverError = (error as { driverError?: unknown }).driverError
|
|
86
|
+
if (driverError && driverError !== error) return isUniqueConstraintError(driverError)
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function findExistingAsnForGoodsReceipt(
|
|
91
|
+
em: EntityManager,
|
|
92
|
+
scope: { organizationId: string; tenantId: string },
|
|
93
|
+
goodsReceiptId: string,
|
|
94
|
+
sourceKey: string,
|
|
95
|
+
): Promise<Asn | null> {
|
|
96
|
+
const bySourceKey = await findOneWithDecryption(
|
|
97
|
+
em,
|
|
98
|
+
Asn,
|
|
99
|
+
{
|
|
100
|
+
organizationId: scope.organizationId,
|
|
101
|
+
tenantId: scope.tenantId,
|
|
102
|
+
sourceKey,
|
|
103
|
+
deletedAt: null,
|
|
104
|
+
},
|
|
105
|
+
undefined,
|
|
106
|
+
scope,
|
|
107
|
+
)
|
|
108
|
+
if (bySourceKey) return bySourceKey
|
|
109
|
+
|
|
110
|
+
// Legacy Story-4 ASNs keyed only by referenceNumber = goodsReceiptId (no source_key).
|
|
111
|
+
return findOneWithDecryption(
|
|
112
|
+
em,
|
|
113
|
+
Asn,
|
|
114
|
+
{
|
|
115
|
+
organizationId: scope.organizationId,
|
|
116
|
+
tenantId: scope.tenantId,
|
|
117
|
+
referenceNumber: goodsReceiptId,
|
|
118
|
+
deletedAt: null,
|
|
119
|
+
},
|
|
120
|
+
undefined,
|
|
121
|
+
scope,
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default async function handle(payload: GoodsReceiptCreatedPayload, ctx: SubscriberContext) {
|
|
126
|
+
const tenantId = asUuid(payload.tenantId)
|
|
127
|
+
const organizationId = asUuid(payload.organizationId)
|
|
128
|
+
if (!tenantId || !organizationId) return
|
|
129
|
+
|
|
130
|
+
const featureTogglesService = tryResolve<FeatureTogglesService>(ctx, 'featureTogglesService')
|
|
131
|
+
const emRoot = tryResolve<EntityManager>(ctx, 'em')
|
|
132
|
+
const commandBus = tryResolve<CommandBus>(ctx, 'commandBus')
|
|
133
|
+
if (!featureTogglesService || !emRoot || !commandBus) return
|
|
134
|
+
|
|
135
|
+
const enabled = await resolveWmsIntegrationToggleEnabled(
|
|
136
|
+
featureTogglesService,
|
|
137
|
+
emRoot,
|
|
138
|
+
'wms_integration_procurement_goods_receipt',
|
|
139
|
+
tenantId,
|
|
140
|
+
)
|
|
141
|
+
if (!enabled) return
|
|
142
|
+
|
|
143
|
+
const warehouseId = asUuid(payload.warehouseId)
|
|
144
|
+
if (!warehouseId) {
|
|
145
|
+
logger.debug('procurement goods receipt skipped: missing warehouseId', {
|
|
146
|
+
goodsReceiptId: payload.id ?? payload.goodsReceiptId ?? null,
|
|
147
|
+
})
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const goodsReceiptId = asUuid(payload.goodsReceiptId) ?? asUuid(payload.id)
|
|
152
|
+
if (!goodsReceiptId) {
|
|
153
|
+
logger.debug('procurement goods receipt skipped: missing goodsReceiptId')
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const sourceKey = buildProcurementGoodsReceiptSourceKey(goodsReceiptId)
|
|
158
|
+
const referenceNumber =
|
|
159
|
+
(typeof payload.referenceNumber === 'string' && payload.referenceNumber.trim().length > 0
|
|
160
|
+
? payload.referenceNumber.trim()
|
|
161
|
+
: null) ?? goodsReceiptId
|
|
162
|
+
|
|
163
|
+
const em = emRoot.fork()
|
|
164
|
+
const scope = { organizationId, tenantId }
|
|
165
|
+
// Match by sourceKey (any status) so retries never create a second ASN.
|
|
166
|
+
const existing = await findExistingAsnForGoodsReceipt(em, scope, goodsReceiptId, sourceKey)
|
|
167
|
+
if (existing) return
|
|
168
|
+
|
|
169
|
+
const expectedAt =
|
|
170
|
+
payload.expectedAt instanceof Date
|
|
171
|
+
? payload.expectedAt
|
|
172
|
+
: typeof payload.expectedAt === 'string' && payload.expectedAt.trim().length > 0
|
|
173
|
+
? new Date(payload.expectedAt)
|
|
174
|
+
: new Date()
|
|
175
|
+
|
|
176
|
+
const lines = (payload.lines ?? [])
|
|
177
|
+
.map((line) => {
|
|
178
|
+
const catalogVariantId = asUuid(line.catalogVariantId)
|
|
179
|
+
const expectedQty = toPositiveQuantity(line.expectedQty)
|
|
180
|
+
if (!catalogVariantId || expectedQty == null) return null
|
|
181
|
+
return {
|
|
182
|
+
catalogVariantId,
|
|
183
|
+
expectedQty,
|
|
184
|
+
lotNumber:
|
|
185
|
+
typeof line.lotNumber === 'string' && line.lotNumber.trim().length > 0
|
|
186
|
+
? line.lotNumber.trim()
|
|
187
|
+
: null,
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
.filter((line): line is NonNullable<typeof line> => line !== null)
|
|
191
|
+
|
|
192
|
+
const commandCtx: CommandRuntimeContext = {
|
|
193
|
+
container: { resolve: ctx.resolve } as CommandRuntimeContext['container'],
|
|
194
|
+
auth: null,
|
|
195
|
+
organizationScope: null,
|
|
196
|
+
selectedOrganizationId: organizationId,
|
|
197
|
+
organizationIds: [organizationId],
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const { translate } = await resolveTranslations()
|
|
201
|
+
const notes = translate(
|
|
202
|
+
'wms.subscribers.procurementGoodsReceipt.asnNotes',
|
|
203
|
+
'Created from procurement goods receipt {goodsReceiptId}',
|
|
204
|
+
{ goodsReceiptId },
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await commandBus.execute('wms.asns.create', {
|
|
209
|
+
input: {
|
|
210
|
+
organizationId,
|
|
211
|
+
tenantId,
|
|
212
|
+
warehouseId,
|
|
213
|
+
vendorId: asUuid(payload.vendorId),
|
|
214
|
+
status: 'draft',
|
|
215
|
+
expectedAt,
|
|
216
|
+
referenceNumber,
|
|
217
|
+
sourceKey,
|
|
218
|
+
notes,
|
|
219
|
+
lines,
|
|
220
|
+
metadata: {
|
|
221
|
+
source: 'procurement.goods_receipt.created',
|
|
222
|
+
goodsReceiptId,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
ctx: commandCtx,
|
|
226
|
+
})
|
|
227
|
+
} catch (err) {
|
|
228
|
+
// Concurrent duplicate events: unique (organization_id, source_key) wins; loser is idempotent.
|
|
229
|
+
if (isUniqueConstraintError(err)) return
|
|
230
|
+
logger.error('procurement goods receipt ASN create failed', { err, goodsReceiptId })
|
|
231
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
232
|
+
module: 'wms',
|
|
233
|
+
code: 'wms.procurement_goods_receipt_asn_create_failed',
|
|
234
|
+
attributes: { goodsReceiptId },
|
|
235
|
+
})
|
|
236
|
+
throw err
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { reevaluateReservationsAfterStockIncrease } from '../lib/salesOrderInventoryAutomation'
|
|
2
|
+
|
|
3
|
+
export const metadata = {
|
|
4
|
+
event: 'wms.putaway.completed',
|
|
5
|
+
persistent: true,
|
|
6
|
+
id: 'wms:putaway-completed-reevaluate-reservations',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type SubscriberContext = {
|
|
10
|
+
resolve: <T = unknown>(name: string) => T
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type PutawayCompletedPayload = {
|
|
14
|
+
catalogVariantId?: string | null
|
|
15
|
+
tenantId?: string | null
|
|
16
|
+
organizationId?: string | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default async function handle(payload: PutawayCompletedPayload, ctx: SubscriberContext) {
|
|
20
|
+
await reevaluateReservationsAfterStockIncrease(payload, ctx)
|
|
21
|
+
}
|
|
@@ -42,6 +42,16 @@ type SalesOrderWmsPayload = {
|
|
|
42
42
|
reservationSummary?: {
|
|
43
43
|
status?: ReservationStatus
|
|
44
44
|
reservationIds?: string[]
|
|
45
|
+
reservations?: Array<{
|
|
46
|
+
id?: string
|
|
47
|
+
reservationLabel?: string
|
|
48
|
+
catalogVariantId?: string
|
|
49
|
+
warehouseId?: string | null
|
|
50
|
+
warehouseName?: string | null
|
|
51
|
+
quantity?: string
|
|
52
|
+
variantName?: string | null
|
|
53
|
+
variantSku?: string | null
|
|
54
|
+
}>
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
|
|
@@ -113,6 +123,51 @@ export default function SalesOrderStockContextWidget(
|
|
|
113
123
|
[wms?.stockSummary],
|
|
114
124
|
)
|
|
115
125
|
|
|
126
|
+
const activeReservations = React.useMemo(() => {
|
|
127
|
+
const enriched = Array.isArray(wms?.reservationSummary?.reservations)
|
|
128
|
+
? (wms.reservationSummary?.reservations ?? [])
|
|
129
|
+
: []
|
|
130
|
+
const fromEnriched = enriched
|
|
131
|
+
.map((item) => {
|
|
132
|
+
const id = typeof item?.id === 'string' ? item.id : null
|
|
133
|
+
if (!id) return null
|
|
134
|
+
const reservationLabel =
|
|
135
|
+
typeof item.reservationLabel === 'string' && item.reservationLabel.trim().length > 0
|
|
136
|
+
? item.reservationLabel.trim()
|
|
137
|
+
: id
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
reservationLabel,
|
|
141
|
+
warehouse_name: item.warehouseName ?? null,
|
|
142
|
+
variant_name: item.variantName ?? null,
|
|
143
|
+
variant_sku: item.variantSku ?? null,
|
|
144
|
+
quantity: item.quantity ?? null,
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
.filter((item): item is NonNullable<typeof item> => item !== null)
|
|
148
|
+
if (fromEnriched.length > 0) return fromEnriched
|
|
149
|
+
|
|
150
|
+
const ids = Array.isArray(wms?.reservationSummary?.reservationIds)
|
|
151
|
+
? (wms.reservationSummary?.reservationIds ?? [])
|
|
152
|
+
: []
|
|
153
|
+
return ids
|
|
154
|
+
.filter((id): id is string => typeof id === 'string' && id.length > 0)
|
|
155
|
+
.map((id) => ({
|
|
156
|
+
id,
|
|
157
|
+
reservationLabel: id,
|
|
158
|
+
warehouse_name: null,
|
|
159
|
+
variant_name: null,
|
|
160
|
+
variant_sku: null,
|
|
161
|
+
quantity: null,
|
|
162
|
+
}))
|
|
163
|
+
}, [wms?.reservationSummary?.reservationIds, wms?.reservationSummary?.reservations])
|
|
164
|
+
|
|
165
|
+
const releaseReservation = React.useMemo(() => {
|
|
166
|
+
if (!releaseReservationId) return null
|
|
167
|
+
const match = activeReservations.find((item) => item.id === releaseReservationId)
|
|
168
|
+
return match ?? { id: releaseReservationId }
|
|
169
|
+
}, [activeReservations, releaseReservationId])
|
|
170
|
+
|
|
116
171
|
React.useEffect(() => {
|
|
117
172
|
const variantIds = stockSummary
|
|
118
173
|
.map((item) => item.catalogVariantId)
|
|
@@ -255,13 +310,6 @@ export default function SalesOrderStockContextWidget(
|
|
|
255
310
|
if (!wms) return null
|
|
256
311
|
|
|
257
312
|
const reservationStatus = wms.reservationSummary?.status ?? 'unreserved'
|
|
258
|
-
const reservationIds = Array.isArray(wms.reservationSummary?.reservationIds)
|
|
259
|
-
? (wms.reservationSummary?.reservationIds ?? [])
|
|
260
|
-
: []
|
|
261
|
-
|
|
262
|
-
const releaseReservation = releaseReservationId
|
|
263
|
-
? { id: releaseReservationId }
|
|
264
|
-
: null
|
|
265
313
|
|
|
266
314
|
return (
|
|
267
315
|
<div className="rounded-lg border bg-card px-4 py-3 space-y-4">
|
|
@@ -344,7 +392,7 @@ export default function SalesOrderStockContextWidget(
|
|
|
344
392
|
'Reservation records',
|
|
345
393
|
)}
|
|
346
394
|
</dt>
|
|
347
|
-
<dd className="font-medium">{
|
|
395
|
+
<dd className="font-medium">{activeReservations.length}</dd>
|
|
348
396
|
</div>
|
|
349
397
|
</dl>
|
|
350
398
|
|
|
@@ -398,7 +446,7 @@ export default function SalesOrderStockContextWidget(
|
|
|
398
446
|
</p>
|
|
399
447
|
)}
|
|
400
448
|
|
|
401
|
-
{access.canRelease &&
|
|
449
|
+
{access.canRelease && activeReservations.length > 0 ? (
|
|
402
450
|
<div className="space-y-2">
|
|
403
451
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
404
452
|
{t(
|
|
@@ -407,18 +455,20 @@ export default function SalesOrderStockContextWidget(
|
|
|
407
455
|
)}
|
|
408
456
|
</p>
|
|
409
457
|
<div className="space-y-1">
|
|
410
|
-
{
|
|
458
|
+
{activeReservations.map((reservation) => (
|
|
411
459
|
<div
|
|
412
|
-
key={
|
|
460
|
+
key={reservation.id}
|
|
413
461
|
className="flex items-center justify-between gap-2 rounded-md border border-border/70 bg-background px-3 py-1.5"
|
|
414
462
|
>
|
|
415
|
-
<span className="
|
|
463
|
+
<span className="text-xs text-foreground truncate" title={reservation.reservationLabel}>
|
|
464
|
+
{reservation.reservationLabel}
|
|
465
|
+
</span>
|
|
416
466
|
<Button
|
|
417
467
|
type="button"
|
|
418
468
|
variant="ghost"
|
|
419
469
|
size="sm"
|
|
420
470
|
className="h-6 shrink-0 px-2 text-xs text-status-error-fg hover:text-status-error-fg"
|
|
421
|
-
onClick={() => setReleaseReservationId(
|
|
471
|
+
onClick={() => setReleaseReservationId(reservation.id)}
|
|
422
472
|
>
|
|
423
473
|
<ShieldOff className="size-3 mr-1" />
|
|
424
474
|
{t('wms.widgets.sales.stockContext.releaseBtn', 'Release')}
|