@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,66 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
3
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
4
|
+
import { putawayTaskCompleteSchema } from '../../../../data/validators'
|
|
5
|
+
import { executeWmsCustomPostRoute } from '../../../inventory/helpers'
|
|
6
|
+
|
|
7
|
+
export const metadata = {
|
|
8
|
+
// Floor ACL: stock-moving operators (`wms.adjust_inventory`) and managers both hold this.
|
|
9
|
+
// Command also allows `wms.manage_putaway` for any task, or adjust + assignee match.
|
|
10
|
+
POST: { requireAuth: true, requireFeatures: ['wms.adjust_inventory'] },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const paramsSchema = z.object({
|
|
14
|
+
id: z.string().uuid(),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export async function POST(request: Request, routeContext: { params: { id: string } }) {
|
|
18
|
+
const parsedParams = paramsSchema.parse(routeContext.params)
|
|
19
|
+
const body = await readJsonSafe<Record<string, unknown>>(request, {})
|
|
20
|
+
const scopedRequest = new Request(request.url, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: request.headers,
|
|
23
|
+
body: JSON.stringify({ ...body, id: parsedParams.id }),
|
|
24
|
+
})
|
|
25
|
+
return executeWmsCustomPostRoute({
|
|
26
|
+
request: scopedRequest,
|
|
27
|
+
routePath: `wms/putaway-tasks/${parsedParams.id}/complete`,
|
|
28
|
+
inputSchema: putawayTaskCompleteSchema,
|
|
29
|
+
commandId: 'wms.putaway-tasks.complete',
|
|
30
|
+
describeResource: (input) => ({
|
|
31
|
+
resourceKind: 'wms.putawayTask',
|
|
32
|
+
resourceId: input.id,
|
|
33
|
+
}),
|
|
34
|
+
mapSuccess: (result: { movementId: string }) => ({
|
|
35
|
+
ok: true,
|
|
36
|
+
movementId: result.movementId,
|
|
37
|
+
}),
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const successSchema = z.object({
|
|
42
|
+
ok: z.literal(true),
|
|
43
|
+
movementId: z.string().uuid(),
|
|
44
|
+
})
|
|
45
|
+
const errorSchema = z.object({ error: z.string() })
|
|
46
|
+
|
|
47
|
+
export const openApi: OpenApiRouteDoc = {
|
|
48
|
+
tag: 'WMS',
|
|
49
|
+
summary: 'Complete putaway task',
|
|
50
|
+
methods: {
|
|
51
|
+
POST: {
|
|
52
|
+
summary: 'Complete putaway task',
|
|
53
|
+
description:
|
|
54
|
+
'Confirms putaway quantity and target location, then moves stock staging→storage via an InventoryMovement of type putaway. Requires `wms.adjust_inventory`. Callers with `wms.manage_putaway` may complete any task; operators may complete only when the task is assigned to them.',
|
|
55
|
+
requestBody: { contentType: 'application/json', schema: putawayTaskCompleteSchema },
|
|
56
|
+
responses: [{ status: 200, description: 'Putaway completed', schema: successSchema }],
|
|
57
|
+
errors: [
|
|
58
|
+
{ status: 400, description: 'Validation failed', schema: errorSchema },
|
|
59
|
+
{ status: 401, description: 'Unauthorized', schema: errorSchema },
|
|
60
|
+
{ status: 403, description: 'Forbidden', schema: errorSchema },
|
|
61
|
+
{ status: 409, description: 'Invalid putaway state or insufficient stock', schema: errorSchema },
|
|
62
|
+
{ status: 422, description: 'Invalid or inactive location', schema: errorSchema },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
3
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
4
|
+
import { putawayTaskStartSchema } from '../../../../data/validators'
|
|
5
|
+
import { executeWmsCustomPostRoute } from '../../../inventory/helpers'
|
|
6
|
+
|
|
7
|
+
export const metadata = {
|
|
8
|
+
POST: { requireAuth: true, requireFeatures: ['wms.manage_putaway'] },
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const paramsSchema = z.object({
|
|
12
|
+
id: z.string().uuid(),
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export async function POST(request: Request, routeContext: { params: { id: string } }) {
|
|
16
|
+
const parsedParams = paramsSchema.parse(routeContext.params)
|
|
17
|
+
const body = await readJsonSafe<Record<string, unknown>>(request, {})
|
|
18
|
+
const scopedRequest = new Request(request.url, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: request.headers,
|
|
21
|
+
body: JSON.stringify({ ...body, id: parsedParams.id }),
|
|
22
|
+
})
|
|
23
|
+
return executeWmsCustomPostRoute({
|
|
24
|
+
request: scopedRequest,
|
|
25
|
+
routePath: `wms/putaway-tasks/${parsedParams.id}/start`,
|
|
26
|
+
inputSchema: putawayTaskStartSchema,
|
|
27
|
+
commandId: 'wms.putaway-tasks.start',
|
|
28
|
+
describeResource: (input) => ({
|
|
29
|
+
resourceKind: 'wms.putawayTask',
|
|
30
|
+
resourceId: input.id,
|
|
31
|
+
}),
|
|
32
|
+
mapSuccess: () => ({ ok: true }),
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const successSchema = z.object({ ok: z.literal(true) })
|
|
37
|
+
const errorSchema = z.object({ error: z.string() })
|
|
38
|
+
|
|
39
|
+
export const openApi: OpenApiRouteDoc = {
|
|
40
|
+
tag: 'WMS',
|
|
41
|
+
summary: 'Start putaway task',
|
|
42
|
+
methods: {
|
|
43
|
+
POST: {
|
|
44
|
+
summary: 'Start putaway task',
|
|
45
|
+
description: 'Marks a putaway task as in progress.',
|
|
46
|
+
requestBody: { contentType: 'application/json', schema: putawayTaskStartSchema },
|
|
47
|
+
responses: [{ status: 200, description: 'Putaway task started', schema: successSchema }],
|
|
48
|
+
errors: [
|
|
49
|
+
{ status: 400, description: 'Validation failed', schema: errorSchema },
|
|
50
|
+
{ status: 401, description: 'Unauthorized', schema: errorSchema },
|
|
51
|
+
{ status: 403, description: 'Forbidden', schema: errorSchema },
|
|
52
|
+
{ status: 409, description: 'Invalid putaway state', schema: errorSchema },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
3
|
+
import { putawayTaskCreateFromBalanceSchema } from '../../../data/validators'
|
|
4
|
+
import { executeWmsCustomPostRoute } from '../../inventory/helpers'
|
|
5
|
+
|
|
6
|
+
export const metadata = {
|
|
7
|
+
POST: { requireAuth: true, requireFeatures: ['wms.manage_putaway'] },
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function POST(request: Request) {
|
|
11
|
+
return executeWmsCustomPostRoute({
|
|
12
|
+
request,
|
|
13
|
+
routePath: 'wms/putaway-tasks/create-from-balance',
|
|
14
|
+
inputSchema: putawayTaskCreateFromBalanceSchema,
|
|
15
|
+
commandId: 'wms.putaway-tasks.create-from-balance',
|
|
16
|
+
describeResource: (input) => ({
|
|
17
|
+
resourceKind: 'wms.putawayTask',
|
|
18
|
+
resourceId: `${input.warehouseId}:${input.sourceLocationId}:${input.catalogVariantId}`,
|
|
19
|
+
}),
|
|
20
|
+
mapSuccess: (result: { taskId: string }) => ({
|
|
21
|
+
ok: true,
|
|
22
|
+
taskId: result.taskId,
|
|
23
|
+
}),
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const successSchema = z.object({
|
|
28
|
+
ok: z.literal(true),
|
|
29
|
+
taskId: z.string().uuid(),
|
|
30
|
+
})
|
|
31
|
+
const errorSchema = z.object({ error: z.string() })
|
|
32
|
+
|
|
33
|
+
export const openApi: OpenApiRouteDoc = {
|
|
34
|
+
tag: 'WMS',
|
|
35
|
+
summary: 'Create putaway task from balance',
|
|
36
|
+
methods: {
|
|
37
|
+
POST: {
|
|
38
|
+
summary: 'Create putaway task from balance',
|
|
39
|
+
description: 'Manually creates an open putaway task against existing staged stock.',
|
|
40
|
+
requestBody: { contentType: 'application/json', schema: putawayTaskCreateFromBalanceSchema },
|
|
41
|
+
responses: [{ status: 200, description: 'Putaway task created', schema: successSchema }],
|
|
42
|
+
errors: [
|
|
43
|
+
{ status: 400, description: 'Validation failed', schema: errorSchema },
|
|
44
|
+
{ status: 401, description: 'Unauthorized', schema: errorSchema },
|
|
45
|
+
{ status: 403, description: 'Forbidden', schema: errorSchema },
|
|
46
|
+
{ status: 422, description: 'Invalid location', schema: errorSchema },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
4
|
+
import { parseScopedCommandInput, resolveCrudRecordId } from '@open-mercato/shared/lib/api/scoped'
|
|
5
|
+
import { E } from '#generated/entities.ids.generated'
|
|
6
|
+
import { PutawayTask } from '../../data/entities'
|
|
7
|
+
import { putawayTaskCreateSchema, putawayTaskUpdateSchema } from '../../data/validators'
|
|
8
|
+
import {
|
|
9
|
+
assertPutawayLifecycleFieldsForbidden,
|
|
10
|
+
buildPutawayTaskStatusFilter,
|
|
11
|
+
} from '../../lib/putaway'
|
|
12
|
+
import { createPagedListResponseSchema, createWmsCrudOpenApi, defaultOkResponseSchema } from '../openapi'
|
|
13
|
+
|
|
14
|
+
const F = {
|
|
15
|
+
id: 'id',
|
|
16
|
+
organization_id: 'organization_id',
|
|
17
|
+
tenant_id: 'tenant_id',
|
|
18
|
+
warehouse_id: 'warehouse_id',
|
|
19
|
+
source_location_id: 'source_location_id',
|
|
20
|
+
target_location_id: 'target_location_id',
|
|
21
|
+
catalog_variant_id: 'catalog_variant_id',
|
|
22
|
+
lot_id: 'lot_id',
|
|
23
|
+
serial_number: 'serial_number',
|
|
24
|
+
quantity: 'quantity',
|
|
25
|
+
status: 'status',
|
|
26
|
+
assigned_to: 'assigned_to',
|
|
27
|
+
priority: 'priority',
|
|
28
|
+
created_at: 'created_at',
|
|
29
|
+
updated_at: 'updated_at',
|
|
30
|
+
} as const
|
|
31
|
+
|
|
32
|
+
const routeMetadata = {
|
|
33
|
+
GET: { requireAuth: true, requireFeatures: ['wms.view'] },
|
|
34
|
+
POST: { requireAuth: true, requireFeatures: ['wms.manage_putaway'] },
|
|
35
|
+
PUT: { requireAuth: true, requireFeatures: ['wms.manage_putaway'] },
|
|
36
|
+
DELETE: { requireAuth: true, requireFeatures: ['wms.manage_putaway'] },
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const metadata = routeMetadata
|
|
40
|
+
|
|
41
|
+
const rawBodySchema = z.object({}).passthrough()
|
|
42
|
+
|
|
43
|
+
const listSchema = z
|
|
44
|
+
.object({
|
|
45
|
+
page: z.coerce.number().min(1).default(1),
|
|
46
|
+
pageSize: z.coerce.number().min(1).max(100).default(25),
|
|
47
|
+
ids: z.string().optional(),
|
|
48
|
+
warehouseId: z.string().uuid().optional(),
|
|
49
|
+
// Single status or comma-separated (e.g. open,in_progress for the active queue).
|
|
50
|
+
status: z.string().optional(),
|
|
51
|
+
assignedTo: z.string().uuid().optional(),
|
|
52
|
+
catalogVariantId: z.string().uuid().optional(),
|
|
53
|
+
sortField: z.string().optional(),
|
|
54
|
+
sortDir: z.enum(['asc', 'desc']).optional(),
|
|
55
|
+
})
|
|
56
|
+
.passthrough()
|
|
57
|
+
|
|
58
|
+
const crud = makeCrudRoute({
|
|
59
|
+
metadata: routeMetadata,
|
|
60
|
+
orm: {
|
|
61
|
+
entity: PutawayTask,
|
|
62
|
+
idField: 'id',
|
|
63
|
+
orgField: 'organizationId',
|
|
64
|
+
tenantField: 'tenantId',
|
|
65
|
+
softDeleteField: 'deletedAt',
|
|
66
|
+
},
|
|
67
|
+
indexer: { entityType: E.wms.putaway_task },
|
|
68
|
+
list: {
|
|
69
|
+
schema: listSchema,
|
|
70
|
+
disableListCache: true,
|
|
71
|
+
entityId: E.wms.putaway_task,
|
|
72
|
+
fields: [
|
|
73
|
+
F.id,
|
|
74
|
+
F.organization_id,
|
|
75
|
+
F.tenant_id,
|
|
76
|
+
F.warehouse_id,
|
|
77
|
+
F.source_location_id,
|
|
78
|
+
F.target_location_id,
|
|
79
|
+
F.catalog_variant_id,
|
|
80
|
+
F.lot_id,
|
|
81
|
+
F.serial_number,
|
|
82
|
+
F.quantity,
|
|
83
|
+
F.status,
|
|
84
|
+
F.assigned_to,
|
|
85
|
+
F.priority,
|
|
86
|
+
F.created_at,
|
|
87
|
+
F.updated_at,
|
|
88
|
+
],
|
|
89
|
+
sortFieldMap: {
|
|
90
|
+
status: F.status,
|
|
91
|
+
priority: F.priority,
|
|
92
|
+
createdAt: F.created_at,
|
|
93
|
+
updatedAt: F.updated_at,
|
|
94
|
+
quantity: F.quantity,
|
|
95
|
+
},
|
|
96
|
+
buildFilters: async (query) => {
|
|
97
|
+
const filters: Record<string, unknown> = {}
|
|
98
|
+
if (typeof query.ids === 'string' && query.ids.trim().length > 0) {
|
|
99
|
+
filters[F.id] = {
|
|
100
|
+
$in: query.ids
|
|
101
|
+
.split(',')
|
|
102
|
+
.map((value) => value.trim())
|
|
103
|
+
.filter((value) => value.length > 0),
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (typeof query.warehouseId === 'string' && query.warehouseId.length > 0) {
|
|
107
|
+
filters[F.warehouse_id] = { $eq: query.warehouseId }
|
|
108
|
+
}
|
|
109
|
+
if (typeof query.status === 'string' && query.status.length > 0) {
|
|
110
|
+
const statusFilter = buildPutawayTaskStatusFilter(query.status)
|
|
111
|
+
if (statusFilter) filters[F.status] = statusFilter
|
|
112
|
+
}
|
|
113
|
+
if (typeof query.assignedTo === 'string' && query.assignedTo.length > 0) {
|
|
114
|
+
filters[F.assigned_to] = { $eq: query.assignedTo }
|
|
115
|
+
}
|
|
116
|
+
if (typeof query.catalogVariantId === 'string' && query.catalogVariantId.length > 0) {
|
|
117
|
+
filters[F.catalog_variant_id] = { $eq: query.catalogVariantId }
|
|
118
|
+
}
|
|
119
|
+
return filters
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
actions: {
|
|
123
|
+
create: {
|
|
124
|
+
commandId: 'wms.putaway-tasks.create',
|
|
125
|
+
schema: rawBodySchema,
|
|
126
|
+
mapInput: async ({ raw, ctx }) => {
|
|
127
|
+
const { translate } = await resolveTranslations()
|
|
128
|
+
return parseScopedCommandInput(putawayTaskCreateSchema, raw ?? {}, ctx, translate)
|
|
129
|
+
},
|
|
130
|
+
response: ({ result }) => ({ id: result?.taskId ?? null }),
|
|
131
|
+
status: 201,
|
|
132
|
+
},
|
|
133
|
+
update: {
|
|
134
|
+
commandId: 'wms.putaway-tasks.update',
|
|
135
|
+
schema: rawBodySchema,
|
|
136
|
+
mapInput: async ({ raw, ctx }) => {
|
|
137
|
+
const { translate } = await resolveTranslations()
|
|
138
|
+
assertPutawayLifecycleFieldsForbidden(raw)
|
|
139
|
+
return parseScopedCommandInput(putawayTaskUpdateSchema, raw ?? {}, ctx, translate)
|
|
140
|
+
},
|
|
141
|
+
response: () => ({ ok: true }),
|
|
142
|
+
},
|
|
143
|
+
delete: {
|
|
144
|
+
commandId: 'wms.putaway-tasks.delete',
|
|
145
|
+
schema: rawBodySchema,
|
|
146
|
+
mapInput: async ({ parsed, ctx }) => {
|
|
147
|
+
const { translate } = await resolveTranslations()
|
|
148
|
+
return { id: resolveCrudRecordId(parsed, ctx, translate) }
|
|
149
|
+
},
|
|
150
|
+
response: () => ({ ok: true }),
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
export const GET = crud.GET
|
|
156
|
+
export const POST = crud.POST
|
|
157
|
+
export const PUT = crud.PUT
|
|
158
|
+
export const DELETE = crud.DELETE
|
|
159
|
+
|
|
160
|
+
const putawayTaskListItemSchema = z.object({
|
|
161
|
+
id: z.string().uuid().nullable().optional(),
|
|
162
|
+
organization_id: z.string().uuid().nullable().optional(),
|
|
163
|
+
tenant_id: z.string().uuid().nullable().optional(),
|
|
164
|
+
warehouse_id: z.string().uuid().nullable().optional(),
|
|
165
|
+
source_location_id: z.string().uuid().nullable().optional(),
|
|
166
|
+
target_location_id: z.string().uuid().nullable().optional(),
|
|
167
|
+
catalog_variant_id: z.string().uuid().nullable().optional(),
|
|
168
|
+
lot_id: z.string().uuid().nullable().optional(),
|
|
169
|
+
serial_number: z.string().nullable().optional(),
|
|
170
|
+
quantity: z.union([z.string(), z.number()]).nullable().optional(),
|
|
171
|
+
status: z.string().nullable().optional(),
|
|
172
|
+
assigned_to: z.string().uuid().nullable().optional(),
|
|
173
|
+
priority: z.union([z.string(), z.number()]).nullable().optional(),
|
|
174
|
+
created_at: z.string().nullable().optional(),
|
|
175
|
+
updated_at: z.string().nullable().optional(),
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
export const openApi = createWmsCrudOpenApi({
|
|
179
|
+
resourceName: 'PutawayTask',
|
|
180
|
+
pluralName: 'Putaway tasks',
|
|
181
|
+
querySchema: listSchema,
|
|
182
|
+
listResponseSchema: createPagedListResponseSchema(putawayTaskListItemSchema),
|
|
183
|
+
create: {
|
|
184
|
+
schema: putawayTaskCreateSchema,
|
|
185
|
+
description:
|
|
186
|
+
'Creates an open putaway task after verifying available staging balance (on-hand minus reserved/allocated minus open/in-progress putaway commitments). Target location may be null until completion. Assignee changes use POST .../assign.',
|
|
187
|
+
},
|
|
188
|
+
update: {
|
|
189
|
+
schema: putawayTaskUpdateSchema,
|
|
190
|
+
responseSchema: defaultOkResponseSchema,
|
|
191
|
+
description:
|
|
192
|
+
'Updates putaway task fields (locations, quantity, priority, metadata). Status and assignedTo are lifecycle-only — use POST .../{assign,start,complete,cancel}.',
|
|
193
|
+
},
|
|
194
|
+
del: {
|
|
195
|
+
schema: z.object({ id: z.string().uuid() }),
|
|
196
|
+
responseSchema: defaultOkResponseSchema,
|
|
197
|
+
description: 'Soft-deletes a putaway task by id.',
|
|
198
|
+
},
|
|
199
|
+
})
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
4
|
+
import { parseScopedCommandInput, resolveCrudRecordId } from '@open-mercato/shared/lib/api/scoped'
|
|
5
|
+
import { E } from '#generated/entities.ids.generated'
|
|
6
|
+
import { ReceivingLine } from '../../data/entities'
|
|
7
|
+
import { receivingLineCreateSchema, receivingLineUpdateSchema } from '../../data/validators'
|
|
8
|
+
import { assertReceivingLineLifecycleFieldsForbidden } from '../../lib/asnReceiving'
|
|
9
|
+
import { createPagedListResponseSchema, createWmsCrudOpenApi, defaultOkResponseSchema } from '../openapi'
|
|
10
|
+
|
|
11
|
+
const F = {
|
|
12
|
+
id: 'id',
|
|
13
|
+
organization_id: 'organization_id',
|
|
14
|
+
tenant_id: 'tenant_id',
|
|
15
|
+
asn_id: 'asn_id',
|
|
16
|
+
catalog_variant_id: 'catalog_variant_id',
|
|
17
|
+
expected_qty: 'expected_qty',
|
|
18
|
+
received_qty: 'received_qty',
|
|
19
|
+
lot_number: 'lot_number',
|
|
20
|
+
serial_numbers: 'serial_numbers',
|
|
21
|
+
qc_status: 'qc_status',
|
|
22
|
+
target_staging_location_id: 'target_staging_location_id',
|
|
23
|
+
rejection_reason: 'rejection_reason',
|
|
24
|
+
created_at: 'created_at',
|
|
25
|
+
updated_at: 'updated_at',
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
const routeMetadata = {
|
|
29
|
+
GET: { requireAuth: true, requireFeatures: ['wms.view'] },
|
|
30
|
+
POST: { requireAuth: true, requireFeatures: ['wms.manage_asn'] },
|
|
31
|
+
PUT: { requireAuth: true, requireFeatures: ['wms.manage_asn'] },
|
|
32
|
+
DELETE: { requireAuth: true, requireFeatures: ['wms.manage_asn'] },
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const metadata = routeMetadata
|
|
36
|
+
|
|
37
|
+
const rawBodySchema = z.object({}).passthrough()
|
|
38
|
+
|
|
39
|
+
const listSchema = z.object({
|
|
40
|
+
page: z.coerce.number().min(1).default(1),
|
|
41
|
+
pageSize: z.coerce.number().min(1).max(100).default(25),
|
|
42
|
+
ids: z.string().optional(),
|
|
43
|
+
asnId: z.string().uuid().optional(),
|
|
44
|
+
catalogVariantId: z.string().uuid().optional(),
|
|
45
|
+
qcStatus: z.enum(['pending', 'passed', 'failed']).optional(),
|
|
46
|
+
sortField: z.string().optional(),
|
|
47
|
+
sortDir: z.enum(['asc', 'desc']).optional(),
|
|
48
|
+
}).passthrough()
|
|
49
|
+
|
|
50
|
+
const crud = makeCrudRoute({
|
|
51
|
+
metadata: routeMetadata,
|
|
52
|
+
orm: {
|
|
53
|
+
entity: ReceivingLine,
|
|
54
|
+
idField: 'id',
|
|
55
|
+
orgField: 'organizationId',
|
|
56
|
+
tenantField: 'tenantId',
|
|
57
|
+
softDeleteField: 'deletedAt',
|
|
58
|
+
},
|
|
59
|
+
indexer: { entityType: E.wms.receiving_line },
|
|
60
|
+
list: {
|
|
61
|
+
schema: listSchema,
|
|
62
|
+
disableListCache: true,
|
|
63
|
+
entityId: E.wms.receiving_line,
|
|
64
|
+
fields: [
|
|
65
|
+
F.id,
|
|
66
|
+
F.organization_id,
|
|
67
|
+
F.tenant_id,
|
|
68
|
+
F.asn_id,
|
|
69
|
+
F.catalog_variant_id,
|
|
70
|
+
F.expected_qty,
|
|
71
|
+
F.received_qty,
|
|
72
|
+
F.lot_number,
|
|
73
|
+
F.serial_numbers,
|
|
74
|
+
F.qc_status,
|
|
75
|
+
F.target_staging_location_id,
|
|
76
|
+
F.rejection_reason,
|
|
77
|
+
F.created_at,
|
|
78
|
+
F.updated_at,
|
|
79
|
+
],
|
|
80
|
+
sortFieldMap: {
|
|
81
|
+
expectedQty: F.expected_qty,
|
|
82
|
+
receivedQty: F.received_qty,
|
|
83
|
+
qcStatus: F.qc_status,
|
|
84
|
+
createdAt: F.created_at,
|
|
85
|
+
updatedAt: F.updated_at,
|
|
86
|
+
},
|
|
87
|
+
buildFilters: async (query) => {
|
|
88
|
+
const filters: Record<string, unknown> = {}
|
|
89
|
+
if (typeof query.ids === 'string' && query.ids.trim().length > 0) {
|
|
90
|
+
filters[F.id] = {
|
|
91
|
+
$in: query.ids.split(',').map((value) => value.trim()).filter((value) => value.length > 0),
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (typeof query.asnId === 'string' && query.asnId.length > 0) {
|
|
95
|
+
filters[F.asn_id] = { $eq: query.asnId }
|
|
96
|
+
}
|
|
97
|
+
if (typeof query.catalogVariantId === 'string' && query.catalogVariantId.length > 0) {
|
|
98
|
+
filters[F.catalog_variant_id] = { $eq: query.catalogVariantId }
|
|
99
|
+
}
|
|
100
|
+
if (typeof query.qcStatus === 'string' && query.qcStatus.length > 0) {
|
|
101
|
+
filters[F.qc_status] = { $eq: query.qcStatus }
|
|
102
|
+
}
|
|
103
|
+
return filters
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
actions: {
|
|
107
|
+
create: {
|
|
108
|
+
commandId: 'wms.receiving-lines.create',
|
|
109
|
+
schema: rawBodySchema,
|
|
110
|
+
mapInput: async ({ raw, ctx }) => {
|
|
111
|
+
const { translate } = await resolveTranslations()
|
|
112
|
+
return parseScopedCommandInput(receivingLineCreateSchema, raw ?? {}, ctx, translate)
|
|
113
|
+
},
|
|
114
|
+
response: ({ result }) => ({ id: result?.lineId ?? null }),
|
|
115
|
+
status: 201,
|
|
116
|
+
},
|
|
117
|
+
update: {
|
|
118
|
+
commandId: 'wms.receiving-lines.update',
|
|
119
|
+
schema: rawBodySchema,
|
|
120
|
+
mapInput: async ({ raw, ctx }) => {
|
|
121
|
+
const { translate } = await resolveTranslations()
|
|
122
|
+
assertReceivingLineLifecycleFieldsForbidden(raw)
|
|
123
|
+
return parseScopedCommandInput(receivingLineUpdateSchema, raw ?? {}, ctx, translate)
|
|
124
|
+
},
|
|
125
|
+
response: () => ({ ok: true }),
|
|
126
|
+
},
|
|
127
|
+
delete: {
|
|
128
|
+
commandId: 'wms.receiving-lines.delete',
|
|
129
|
+
schema: rawBodySchema,
|
|
130
|
+
mapInput: async ({ parsed, ctx }) => {
|
|
131
|
+
const { translate } = await resolveTranslations()
|
|
132
|
+
return { id: resolveCrudRecordId(parsed, ctx, translate) }
|
|
133
|
+
},
|
|
134
|
+
response: () => ({ ok: true }),
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
export const GET = crud.GET
|
|
140
|
+
export const POST = crud.POST
|
|
141
|
+
export const PUT = crud.PUT
|
|
142
|
+
export const DELETE = crud.DELETE
|
|
143
|
+
|
|
144
|
+
const receivingLineListItemSchema = z.object({
|
|
145
|
+
id: z.string().uuid().nullable().optional(),
|
|
146
|
+
organization_id: z.string().uuid().nullable().optional(),
|
|
147
|
+
tenant_id: z.string().uuid().nullable().optional(),
|
|
148
|
+
asn_id: z.string().uuid().nullable().optional(),
|
|
149
|
+
catalog_variant_id: z.string().uuid().nullable().optional(),
|
|
150
|
+
expected_qty: z.union([z.string(), z.number()]).nullable().optional(),
|
|
151
|
+
received_qty: z.union([z.string(), z.number()]).nullable().optional(),
|
|
152
|
+
lot_number: z.string().nullable().optional(),
|
|
153
|
+
serial_numbers: z.array(z.string()).nullable().optional(),
|
|
154
|
+
qc_status: z.string().nullable().optional(),
|
|
155
|
+
target_staging_location_id: z.string().uuid().nullable().optional(),
|
|
156
|
+
rejection_reason: z.string().nullable().optional(),
|
|
157
|
+
created_at: z.string().nullable().optional(),
|
|
158
|
+
updated_at: z.string().nullable().optional(),
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
export const openApi = createWmsCrudOpenApi({
|
|
162
|
+
resourceName: 'ReceivingLine',
|
|
163
|
+
pluralName: 'Receiving lines',
|
|
164
|
+
querySchema: listSchema,
|
|
165
|
+
listResponseSchema: createPagedListResponseSchema(receivingLineListItemSchema),
|
|
166
|
+
create: {
|
|
167
|
+
schema: receivingLineCreateSchema,
|
|
168
|
+
description: 'Creates a receiving line against an ASN.',
|
|
169
|
+
},
|
|
170
|
+
update: {
|
|
171
|
+
schema: receivingLineUpdateSchema,
|
|
172
|
+
responseSchema: defaultOkResponseSchema,
|
|
173
|
+
description: 'Updates a receiving line by id.',
|
|
174
|
+
},
|
|
175
|
+
del: {
|
|
176
|
+
schema: z.object({ id: z.string().uuid() }),
|
|
177
|
+
responseSchema: defaultOkResponseSchema,
|
|
178
|
+
description: 'Soft-deletes a receiving line by id.',
|
|
179
|
+
},
|
|
180
|
+
})
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
4
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
5
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
6
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
7
|
+
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
8
|
+
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
9
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
10
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
11
|
+
import { putawayTaskCompleteSchema, scanPutawaySchema } from '../../../data/validators'
|
|
12
|
+
import { resolveScanPutawayCommandInput } from '../../../lib/scanResolve'
|
|
13
|
+
import { executeWmsCustomPostRoute } from '../../inventory/helpers'
|
|
14
|
+
|
|
15
|
+
const logger = createLogger('wms')
|
|
16
|
+
|
|
17
|
+
export const metadata = {
|
|
18
|
+
// Same floor ACL as putaway-tasks/:id/complete (command enforces manage vs assignee).
|
|
19
|
+
POST: { requireAuth: true, requireFeatures: ['wms.adjust_inventory'] },
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const successSchema = z.object({
|
|
23
|
+
ok: z.literal(true),
|
|
24
|
+
movementId: z.string().uuid(),
|
|
25
|
+
})
|
|
26
|
+
const errorSchema = z.object({ error: z.string() })
|
|
27
|
+
|
|
28
|
+
export async function POST(request: Request) {
|
|
29
|
+
try {
|
|
30
|
+
const container = await createRequestContainer()
|
|
31
|
+
const auth = await getAuthFromRequest(request)
|
|
32
|
+
if (!auth?.tenantId) {
|
|
33
|
+
throw new CrudHttpError(401, { error: 'Unauthorized' })
|
|
34
|
+
}
|
|
35
|
+
const organizationScope = await resolveOrganizationScopeForRequest({
|
|
36
|
+
container,
|
|
37
|
+
auth,
|
|
38
|
+
request,
|
|
39
|
+
})
|
|
40
|
+
const organizationId = organizationScope?.selectedId ?? auth.orgId ?? null
|
|
41
|
+
if (!organizationId) {
|
|
42
|
+
throw new CrudHttpError(400, { error: 'organization_scope_required' })
|
|
43
|
+
}
|
|
44
|
+
const body = await readJsonSafe<Record<string, unknown>>(request, {})
|
|
45
|
+
const parsed = scanPutawaySchema.parse(body)
|
|
46
|
+
if (parsed.tenantId !== auth.tenantId && !auth.isSuperAdmin) {
|
|
47
|
+
throw new CrudHttpError(403, { error: 'Forbidden' })
|
|
48
|
+
}
|
|
49
|
+
// Scope organization from auth/session — never trust body organizationId for
|
|
50
|
+
// task/location resolve (same contract as scan/receive and resolve-location/lot).
|
|
51
|
+
const scoped = {
|
|
52
|
+
...parsed,
|
|
53
|
+
tenantId: auth.tenantId,
|
|
54
|
+
organizationId,
|
|
55
|
+
}
|
|
56
|
+
const em = (container.resolve('em') as EntityManager).fork()
|
|
57
|
+
const commandInput = await resolveScanPutawayCommandInput(em, scoped)
|
|
58
|
+
const scopedRequest = new Request(request.url, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: request.headers,
|
|
61
|
+
body: JSON.stringify(commandInput),
|
|
62
|
+
})
|
|
63
|
+
return executeWmsCustomPostRoute({
|
|
64
|
+
request: scopedRequest,
|
|
65
|
+
routePath: 'wms/scan/putaway',
|
|
66
|
+
inputSchema: putawayTaskCompleteSchema,
|
|
67
|
+
commandId: 'wms.putaway-tasks.complete',
|
|
68
|
+
describeResource: (input) => ({
|
|
69
|
+
resourceKind: 'wms.putawayTask',
|
|
70
|
+
resourceId: input.id,
|
|
71
|
+
}),
|
|
72
|
+
mapSuccess: (result: { movementId: string }) => ({
|
|
73
|
+
ok: true,
|
|
74
|
+
movementId: result.movementId,
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof CrudHttpError) {
|
|
79
|
+
return NextResponse.json(error.body, { status: error.status })
|
|
80
|
+
}
|
|
81
|
+
if (error instanceof z.ZodError) {
|
|
82
|
+
return NextResponse.json({ error: 'Validation failed', details: error.issues }, { status: 400 })
|
|
83
|
+
}
|
|
84
|
+
logger.error('scan putaway failed', { err: error })
|
|
85
|
+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const openApi: OpenApiRouteDoc = {
|
|
90
|
+
tag: 'WMS',
|
|
91
|
+
summary: 'Scan-assisted putaway complete',
|
|
92
|
+
methods: {
|
|
93
|
+
POST: {
|
|
94
|
+
summary: 'Complete putaway from scanned target location code',
|
|
95
|
+
description:
|
|
96
|
+
'Resolves the scanned target location code, then executes the same command path as POST /api/wms/putaway-tasks/:id/complete. Organization is taken from the authenticated session scope, not the request body. Requires `wms.adjust_inventory`; managers with `wms.manage_putaway` may complete any task, operators only when assigned.',
|
|
97
|
+
requestBody: { contentType: 'application/json', schema: scanPutawaySchema },
|
|
98
|
+
responses: [{ status: 200, description: 'Putaway completed', schema: successSchema }],
|
|
99
|
+
errors: [
|
|
100
|
+
{ status: 400, description: 'Validation failed', schema: errorSchema },
|
|
101
|
+
{ status: 401, description: 'Unauthorized', schema: errorSchema },
|
|
102
|
+
{ status: 404, description: 'Task or location not found', schema: errorSchema },
|
|
103
|
+
{ status: 409, description: 'Invalid putaway state or insufficient stock', schema: errorSchema },
|
|
104
|
+
{ status: 422, description: 'Invalid or inactive location', schema: errorSchema },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
}
|