@open-mercato/core 0.9.1-develop.7499.1.fcec456e4e → 0.9.1-develop.7515.1.e2712c8b00
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 +1 -1
- package/dist/modules/audit_logs/components/AuditLogsActions.js +1 -1
- package/dist/modules/audit_logs/components/AuditLogsActions.js.map +2 -2
- package/dist/modules/auth/frontend/reset.js +7 -3
- package/dist/modules/auth/frontend/reset.js.map +2 -2
- package/dist/modules/business_rules/api/rules/route.js +12 -1
- package/dist/modules/business_rules/api/rules/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +17 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +3 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +1 -0
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/components/products/productFormUtils.js +9 -0
- package/dist/modules/catalog/components/products/productFormUtils.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +32 -32
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +22 -22
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +34 -34
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/ecommerce/search.js +1 -2
- package/dist/modules/ecommerce/search.js.map +2 -2
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +4 -7
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +29 -11
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/status-sync.js +2 -2
- package/dist/modules/shipping_carriers/lib/status-sync.js.map +2 -2
- package/dist/modules/shipping_carriers/workers/webhook-processor.js +60 -33
- package/dist/modules/shipping_carriers/workers/webhook-processor.js.map +3 -3
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.js +10 -7
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.js.map +2 -2
- package/dist/modules/warranty_claims/commands/claim-lines.js +89 -68
- package/dist/modules/warranty_claims/commands/claim-lines.js.map +2 -2
- package/dist/modules/warranty_claims/commands/claims.js +14 -13
- package/dist/modules/warranty_claims/commands/claims.js.map +2 -2
- package/dist/modules/warranty_claims/commands/shared.js +8 -2
- package/dist/modules/warranty_claims/commands/shared.js.map +2 -2
- package/dist/modules/wms/api/interceptors.js +3 -0
- package/dist/modules/wms/api/interceptors.js.map +2 -2
- package/dist/modules/wms/commands/configuration.js +16 -16
- package/dist/modules/wms/commands/configuration.js.map +2 -2
- package/dist/modules/wms/events.js +1 -0
- package/dist/modules/wms/events.js.map +2 -2
- package/dist/modules/wms/lib/catalogInventoryProfileIntent.js +9 -0
- package/dist/modules/wms/lib/catalogInventoryProfileIntent.js.map +2 -2
- package/dist/modules/wms/widgets/injection/catalog-inventory-profile/widget.js +11 -3
- package/dist/modules/wms/widgets/injection/catalog-inventory-profile/widget.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/audit_logs/components/AuditLogsActions.tsx +1 -1
- package/src/modules/auth/frontend/reset.tsx +8 -3
- package/src/modules/business_rules/api/rules/route.ts +11 -0
- package/src/modules/business_rules/i18n/de.json +1 -0
- package/src/modules/business_rules/i18n/en.json +1 -0
- package/src/modules/business_rules/i18n/es.json +1 -0
- package/src/modules/business_rules/i18n/ko.json +1 -0
- package/src/modules/business_rules/i18n/pl.json +1 -0
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +20 -1
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +2 -1
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +1 -0
- package/src/modules/catalog/components/products/productFormUtils.ts +9 -0
- package/src/modules/customers/backend/customers/companies/page.tsx +32 -32
- package/src/modules/customers/backend/customers/deals/page.tsx +22 -22
- package/src/modules/customers/backend/customers/people/page.tsx +34 -34
- package/src/modules/customers/i18n/de.json +12 -0
- package/src/modules/customers/i18n/en.json +12 -0
- package/src/modules/customers/i18n/es.json +12 -0
- package/src/modules/customers/i18n/ko.json +12 -0
- package/src/modules/customers/i18n/pl.json +12 -0
- package/src/modules/ecommerce/i18n/de.json +2 -1
- package/src/modules/ecommerce/i18n/en.json +2 -1
- package/src/modules/ecommerce/i18n/es.json +2 -1
- package/src/modules/ecommerce/i18n/ko.json +2 -1
- package/src/modules/ecommerce/i18n/pl.json +2 -1
- package/src/modules/ecommerce/search.ts +0 -1
- package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +4 -7
- package/src/modules/shipping_carriers/lib/shipping-service.ts +29 -11
- package/src/modules/shipping_carriers/lib/status-sync.ts +2 -2
- package/src/modules/shipping_carriers/workers/webhook-processor.ts +71 -39
- package/src/modules/staff/backend/staff/time-tracking/entries/page.tsx +20 -14
- package/src/modules/warranty_claims/commands/claim-lines.ts +107 -66
- package/src/modules/warranty_claims/commands/claims.ts +14 -13
- package/src/modules/warranty_claims/commands/shared.ts +7 -1
- package/src/modules/wms/api/interceptors.ts +9 -0
- package/src/modules/wms/commands/configuration.ts +28 -16
- package/src/modules/wms/events.ts +1 -0
- package/src/modules/wms/lib/catalogInventoryProfileIntent.ts +9 -0
- package/src/modules/wms/widgets/injection/catalog-inventory-profile/widget.ts +13 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/wms/events.ts"],
|
|
4
|
-
"sourcesContent": ["import { createModuleEvents } from '@open-mercato/shared/modules/events'\n\nconst events = [\n { id: 'wms.warehouse.created', label: 'Warehouse Created', entity: 'warehouse', category: 'crud' },\n { id: 'wms.warehouse.updated', label: 'Warehouse Updated', entity: 'warehouse', category: 'crud' },\n { id: 'wms.zone.created', label: 'Warehouse Zone Created', entity: 'zone', category: 'crud' },\n { id: 'wms.zone.updated', label: 'Warehouse Zone Updated', entity: 'zone', category: 'crud' },\n { id: 'wms.location.created', label: 'Warehouse Location Created', entity: 'location', category: 'crud' },\n { id: 'wms.location.updated', label: 'Warehouse Location Updated', entity: 'location', category: 'crud' },\n { id: 'wms.inventory_profile.created', label: 'Inventory Profile Created', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_profile.updated', label: 'Inventory Profile Updated', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_balance.created', label: 'Inventory Balance Created', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.updated', label: 'Inventory Balance Updated', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.deleted', label: 'Inventory Balance Deleted', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_reservation.created', label: 'Inventory Reservation Created', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.updated', label: 'Inventory Reservation Updated', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.deleted', label: 'Inventory Reservation Deleted', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_movement.created', label: 'Inventory Movement Created', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.updated', label: 'Inventory Movement Updated', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.deleted', label: 'Inventory Movement Deleted', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory.received', label: 'Inventory Received', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.adjusted', label: 'Inventory Adjusted', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reserved', label: 'Inventory Reserved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.released', label: 'Inventory Released', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.allocated', label: 'Inventory Allocated', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.moved', label: 'Inventory Moved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reconciled', label: 'Inventory Reconciled', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.low_stock', label: 'Inventory Low Stock', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.balance_drift', label: 'Inventory Balance Drift', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.reservation_shortfall', label: 'Inventory Reservation Shortfall', entity: 'inventory', category: 'lifecycle' },\n] as const\n\nexport const eventsConfig = createModuleEvents({\n moduleId: 'wms',\n events,\n})\n\nexport const emitWmsEvent = eventsConfig.emit\nexport type WmsEventId = typeof events[number]['id']\n\nexport default eventsConfig\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,0BAA0B;AAEnC,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,yBAAyB,OAAO,qBAAqB,QAAQ,aAAa,UAAU,OAAO;AAAA,EACjG,EAAE,IAAI,yBAAyB,OAAO,qBAAqB,QAAQ,aAAa,UAAU,OAAO;AAAA,EACjG,EAAE,IAAI,oBAAoB,OAAO,0BAA0B,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC5F,EAAE,IAAI,oBAAoB,OAAO,0BAA0B,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC5F,EAAE,IAAI,wBAAwB,OAAO,8BAA8B,QAAQ,YAAY,UAAU,OAAO;AAAA,EACxG,EAAE,IAAI,wBAAwB,OAAO,8BAA8B,QAAQ,YAAY,UAAU,OAAO;AAAA,EACxG,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,2BAA2B,OAAO,uBAAuB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACvG,EAAE,IAAI,uBAAuB,OAAO,mBAAmB,QAAQ,aAAa,UAAU,SAAS;AAAA,EAC/F,EAAE,IAAI,4BAA4B,OAAO,wBAAwB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACzG,EAAE,IAAI,2BAA2B,OAAO,uBAAuB,QAAQ,aAAa,UAAU,YAAY;AAAA,EAC1G,EAAE,IAAI,+BAA+B,OAAO,2BAA2B,QAAQ,aAAa,UAAU,YAAY;AAAA,EAClH,EAAE,IAAI,uCAAuC,OAAO,mCAAmC,QAAQ,aAAa,UAAU,YAAY;AACpI;AAEO,MAAM,eAAe,mBAAmB;AAAA,EAC7C,UAAU;AAAA,EACV;AACF,CAAC;AAEM,MAAM,eAAe,aAAa;AAGzC,IAAO,iBAAQ;",
|
|
4
|
+
"sourcesContent": ["import { createModuleEvents } from '@open-mercato/shared/modules/events'\n\nconst events = [\n { id: 'wms.warehouse.created', label: 'Warehouse Created', entity: 'warehouse', category: 'crud' },\n { id: 'wms.warehouse.updated', label: 'Warehouse Updated', entity: 'warehouse', category: 'crud' },\n { id: 'wms.zone.created', label: 'Warehouse Zone Created', entity: 'zone', category: 'crud' },\n { id: 'wms.zone.updated', label: 'Warehouse Zone Updated', entity: 'zone', category: 'crud' },\n { id: 'wms.location.created', label: 'Warehouse Location Created', entity: 'location', category: 'crud' },\n { id: 'wms.location.updated', label: 'Warehouse Location Updated', entity: 'location', category: 'crud' },\n { id: 'wms.inventory_profile.created', label: 'Inventory Profile Created', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_profile.updated', label: 'Inventory Profile Updated', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_profile.deleted', label: 'Inventory Profile Deleted', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_balance.created', label: 'Inventory Balance Created', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.updated', label: 'Inventory Balance Updated', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.deleted', label: 'Inventory Balance Deleted', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_reservation.created', label: 'Inventory Reservation Created', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.updated', label: 'Inventory Reservation Updated', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.deleted', label: 'Inventory Reservation Deleted', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_movement.created', label: 'Inventory Movement Created', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.updated', label: 'Inventory Movement Updated', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.deleted', label: 'Inventory Movement Deleted', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory.received', label: 'Inventory Received', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.adjusted', label: 'Inventory Adjusted', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reserved', label: 'Inventory Reserved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.released', label: 'Inventory Released', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.allocated', label: 'Inventory Allocated', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.moved', label: 'Inventory Moved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reconciled', label: 'Inventory Reconciled', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.low_stock', label: 'Inventory Low Stock', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.balance_drift', label: 'Inventory Balance Drift', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.reservation_shortfall', label: 'Inventory Reservation Shortfall', entity: 'inventory', category: 'lifecycle' },\n] as const\n\nexport const eventsConfig = createModuleEvents({\n moduleId: 'wms',\n events,\n})\n\nexport const emitWmsEvent = eventsConfig.emit\nexport type WmsEventId = typeof events[number]['id']\n\nexport default eventsConfig\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,0BAA0B;AAEnC,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,yBAAyB,OAAO,qBAAqB,QAAQ,aAAa,UAAU,OAAO;AAAA,EACjG,EAAE,IAAI,yBAAyB,OAAO,qBAAqB,QAAQ,aAAa,UAAU,OAAO;AAAA,EACjG,EAAE,IAAI,oBAAoB,OAAO,0BAA0B,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC5F,EAAE,IAAI,oBAAoB,OAAO,0BAA0B,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC5F,EAAE,IAAI,wBAAwB,OAAO,8BAA8B,QAAQ,YAAY,UAAU,OAAO;AAAA,EACxG,EAAE,IAAI,wBAAwB,OAAO,8BAA8B,QAAQ,YAAY,UAAU,OAAO;AAAA,EACxG,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,2BAA2B,OAAO,uBAAuB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACvG,EAAE,IAAI,uBAAuB,OAAO,mBAAmB,QAAQ,aAAa,UAAU,SAAS;AAAA,EAC/F,EAAE,IAAI,4BAA4B,OAAO,wBAAwB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACzG,EAAE,IAAI,2BAA2B,OAAO,uBAAuB,QAAQ,aAAa,UAAU,YAAY;AAAA,EAC1G,EAAE,IAAI,+BAA+B,OAAO,2BAA2B,QAAQ,aAAa,UAAU,YAAY;AAAA,EAClH,EAAE,IAAI,uCAAuC,OAAO,mCAAmC,QAAQ,aAAa,UAAU,YAAY;AACpI;AAEO,MAAM,eAAe,mBAAmB;AAAA,EAC7C,UAAU;AAAA,EACV;AACF,CAAC;AAEM,MAAM,eAAe,aAAa;AAGzC,IAAO,iBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
const inventoryStrategySchema = z.enum(["fifo", "lifo", "fefo"]);
|
|
3
3
|
const catalogInventoryProfileIntentSchema = z.object({
|
|
4
|
+
// Identifies which host CrudForm (product vs. variant) computed this intent.
|
|
5
|
+
// CrudForm scopes `onBeforeSave` request headers around the whole `onSubmit`
|
|
6
|
+
// callback, not around a single HTTP call, so a page whose submit handler
|
|
7
|
+
// creates secondary records (e.g. the product-create wizard implicitly
|
|
8
|
+
// creating its default variant) can leak this header onto those unrelated
|
|
9
|
+
// requests. Each interceptor only acts when this matches its own target
|
|
10
|
+
// entity, so a leaked intent is ignored rather than creating a profile the
|
|
11
|
+
// user never asked for on that other entity.
|
|
12
|
+
sourceEntityId: z.string(),
|
|
4
13
|
manageInventory: z.boolean(),
|
|
5
14
|
defaultUom: z.string().trim().max(32).nullable().optional(),
|
|
6
15
|
defaultStrategy: inventoryStrategySchema.optional(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/wms/lib/catalogInventoryProfileIntent.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\n\nconst inventoryStrategySchema = z.enum(['fifo', 'lifo', 'fefo'])\n\nexport const catalogInventoryProfileIntentSchema = z.object({\n manageInventory: z.boolean(),\n defaultUom: z.string().trim().max(32).nullable().optional(),\n defaultStrategy: inventoryStrategySchema.optional(),\n trackLot: z.boolean().optional(),\n trackSerial: z.boolean().optional(),\n trackExpiration: z.boolean().optional(),\n reorderPoint: z.coerce.number().finite().min(0).nullable().optional(),\n safetyStock: z.coerce.number().finite().min(0).nullable().optional(),\n}).superRefine((payload, ctx) => {\n if (!payload.manageInventory) return\n\n if (!payload.defaultUom || payload.defaultUom.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultUom'],\n message: 'wms.widgets.catalog.inventoryProfile.errors.defaultUomRequired',\n })\n }\n\n if (!payload.defaultStrategy) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultStrategy'],\n message: 'wms.widgets.catalog.inventoryProfile.errors.strategyRequired',\n })\n }\n\n if (payload.trackExpiration && payload.defaultStrategy !== 'fefo') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultStrategy'],\n message: 'wms.widgets.catalog.inventoryProfile.errors.fefoRequired',\n })\n }\n})\n\nexport type CatalogInventoryProfileIntent = z.infer<\n typeof catalogInventoryProfileIntentSchema\n>\n\nexport const WMS_CATALOG_PROFILE_HEADER =\n 'x-open-mercato-wms-catalog-profile'\n\nexport function encodeCatalogInventoryProfileIntent(\n intent: CatalogInventoryProfileIntent,\n): string {\n return encodeURIComponent(JSON.stringify(intent))\n}\n\nexport function decodeCatalogInventoryProfileIntent(\n rawHeader: string | null | undefined,\n): CatalogInventoryProfileIntent | null {\n if (typeof rawHeader !== 'string' || rawHeader.trim().length === 0) {\n return null\n }\n\n const decoded = decodeURIComponent(rawHeader)\n const parsed = JSON.parse(decoded) as unknown\n return catalogInventoryProfileIntentSchema.parse(parsed)\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAElB,MAAM,0BAA0B,EAAE,KAAK,CAAC,QAAQ,QAAQ,MAAM,CAAC;AAExD,MAAM,sCAAsC,EAAE,OAAO;AAAA,
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\n\nconst inventoryStrategySchema = z.enum(['fifo', 'lifo', 'fefo'])\n\nexport const catalogInventoryProfileIntentSchema = z.object({\n // Identifies which host CrudForm (product vs. variant) computed this intent.\n // CrudForm scopes `onBeforeSave` request headers around the whole `onSubmit`\n // callback, not around a single HTTP call, so a page whose submit handler\n // creates secondary records (e.g. the product-create wizard implicitly\n // creating its default variant) can leak this header onto those unrelated\n // requests. Each interceptor only acts when this matches its own target\n // entity, so a leaked intent is ignored rather than creating a profile the\n // user never asked for on that other entity.\n sourceEntityId: z.string(),\n manageInventory: z.boolean(),\n defaultUom: z.string().trim().max(32).nullable().optional(),\n defaultStrategy: inventoryStrategySchema.optional(),\n trackLot: z.boolean().optional(),\n trackSerial: z.boolean().optional(),\n trackExpiration: z.boolean().optional(),\n reorderPoint: z.coerce.number().finite().min(0).nullable().optional(),\n safetyStock: z.coerce.number().finite().min(0).nullable().optional(),\n}).superRefine((payload, ctx) => {\n if (!payload.manageInventory) return\n\n if (!payload.defaultUom || payload.defaultUom.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultUom'],\n message: 'wms.widgets.catalog.inventoryProfile.errors.defaultUomRequired',\n })\n }\n\n if (!payload.defaultStrategy) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultStrategy'],\n message: 'wms.widgets.catalog.inventoryProfile.errors.strategyRequired',\n })\n }\n\n if (payload.trackExpiration && payload.defaultStrategy !== 'fefo') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultStrategy'],\n message: 'wms.widgets.catalog.inventoryProfile.errors.fefoRequired',\n })\n }\n})\n\nexport type CatalogInventoryProfileIntent = z.infer<\n typeof catalogInventoryProfileIntentSchema\n>\n\nexport const WMS_CATALOG_PROFILE_HEADER =\n 'x-open-mercato-wms-catalog-profile'\n\nexport function encodeCatalogInventoryProfileIntent(\n intent: CatalogInventoryProfileIntent,\n): string {\n return encodeURIComponent(JSON.stringify(intent))\n}\n\nexport function decodeCatalogInventoryProfileIntent(\n rawHeader: string | null | undefined,\n): CatalogInventoryProfileIntent | null {\n if (typeof rawHeader !== 'string' || rawHeader.trim().length === 0) {\n return null\n }\n\n const decoded = decodeURIComponent(rawHeader)\n const parsed = JSON.parse(decoded) as unknown\n return catalogInventoryProfileIntentSchema.parse(parsed)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAElB,MAAM,0BAA0B,EAAE,KAAK,CAAC,QAAQ,QAAQ,MAAM,CAAC;AAExD,MAAM,sCAAsC,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS1D,gBAAgB,EAAE,OAAO;AAAA,EACzB,iBAAiB,EAAE,QAAQ;AAAA,EAC3B,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1D,iBAAiB,wBAAwB,SAAS;AAAA,EAClD,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC/B,aAAa,EAAE,QAAQ,EAAE,SAAS;AAAA,EAClC,iBAAiB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACtC,cAAc,EAAE,OAAO,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACpE,aAAa,EAAE,OAAO,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS;AACrE,CAAC,EAAE,YAAY,CAAC,SAAS,QAAQ;AAC/B,MAAI,CAAC,QAAQ,gBAAiB;AAE9B,MAAI,CAAC,QAAQ,cAAc,QAAQ,WAAW,KAAK,EAAE,WAAW,GAAG;AACjE,QAAI,SAAS;AAAA,MACX,MAAM,EAAE,aAAa;AAAA,MACrB,MAAM,CAAC,YAAY;AAAA,MACnB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,QAAQ,iBAAiB;AAC5B,QAAI,SAAS;AAAA,MACX,MAAM,EAAE,aAAa;AAAA,MACrB,MAAM,CAAC,iBAAiB;AAAA,MACxB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,MAAI,QAAQ,mBAAmB,QAAQ,oBAAoB,QAAQ;AACjE,QAAI,SAAS;AAAA,MACX,MAAM,EAAE,aAAa;AAAA,MACrB,MAAM,CAAC,iBAAiB;AAAA,MACxB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF,CAAC;AAMM,MAAM,6BACX;AAEK,SAAS,oCACd,QACQ;AACR,SAAO,mBAAmB,KAAK,UAAU,MAAM,CAAC;AAClD;AAEO,SAAS,oCACd,WACsC;AACtC,MAAI,OAAO,cAAc,YAAY,UAAU,KAAK,EAAE,WAAW,GAAG;AAClE,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,mBAAmB,SAAS;AAC5C,QAAM,SAAS,KAAK,MAAM,OAAO;AACjC,SAAO,oCAAoC,MAAM,MAAM;AACzD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,8 +20,9 @@ function readNumber(value) {
|
|
|
20
20
|
}
|
|
21
21
|
return void 0;
|
|
22
22
|
}
|
|
23
|
-
function buildIntent(data) {
|
|
23
|
+
function buildIntent(data, sourceEntityId) {
|
|
24
24
|
return {
|
|
25
|
+
sourceEntityId,
|
|
25
26
|
manageInventory: readBoolean(data["wms.manageInventory"]),
|
|
26
27
|
defaultUom: readString(data["wms.defaultUom"]).trim() || null,
|
|
27
28
|
defaultStrategy: (() => {
|
|
@@ -35,6 +36,9 @@ function buildIntent(data) {
|
|
|
35
36
|
safetyStock: readNumber(data["wms.safetyStock"]) ?? 0
|
|
36
37
|
};
|
|
37
38
|
}
|
|
39
|
+
function hasLoadedInventoryProfile(data) {
|
|
40
|
+
return Boolean(asRecord(asRecord(data._wms)?.inventoryProfile));
|
|
41
|
+
}
|
|
38
42
|
function buildDisplayState(data) {
|
|
39
43
|
const wms = asRecord(data._wms);
|
|
40
44
|
const inventoryProfile = asRecord(wms?.inventoryProfile);
|
|
@@ -150,11 +154,15 @@ const widget = {
|
|
|
150
154
|
if (!source) return data;
|
|
151
155
|
return buildDisplayState(source);
|
|
152
156
|
},
|
|
153
|
-
async onBeforeSave(data) {
|
|
157
|
+
async onBeforeSave(data, context) {
|
|
154
158
|
const source = asRecord(data);
|
|
155
159
|
if (!source) return { ok: true };
|
|
156
|
-
const
|
|
160
|
+
const sourceEntityId = readString(context?.entityId);
|
|
161
|
+
const intent = buildIntent(source, sourceEntityId);
|
|
157
162
|
if (!intent.manageInventory) {
|
|
163
|
+
if (source["wms.manageInventory"] !== false || !hasLoadedInventoryProfile(source)) {
|
|
164
|
+
return { ok: true };
|
|
165
|
+
}
|
|
158
166
|
return {
|
|
159
167
|
ok: true,
|
|
160
168
|
requestHeaders: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/wms/widgets/injection/catalog-inventory-profile/widget.ts"],
|
|
4
|
-
"sourcesContent": ["import type { InjectionFieldWidget } from '@open-mercato/shared/modules/widgets/injection'\nimport {\n encodeCatalogInventoryProfileIntent,\n WMS_CATALOG_PROFILE_HEADER,\n type CatalogInventoryProfileIntent,\n} from '../../../lib/catalogInventoryProfileIntent'\n\ntype RecordLike = Record<string, unknown>\n\nfunction asRecord(value: unknown): RecordLike | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n return value as RecordLike\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction readBoolean(value: unknown): boolean {\n return value === true\n}\n\nfunction readNumber(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n return undefined\n}\n\nfunction buildIntent(data: RecordLike): CatalogInventoryProfileIntent {\n return {\n manageInventory: readBoolean(data['wms.manageInventory']),\n defaultUom: readString(data['wms.defaultUom']).trim() || null,\n defaultStrategy: (() => {\n const strategy = readString(data['wms.defaultStrategy']).trim()\n return strategy === 'fifo' || strategy === 'lifo' || strategy === 'fefo'\n ? strategy\n : undefined\n })(),\n trackLot: readBoolean(data['wms.trackLot']),\n trackSerial: readBoolean(data['wms.trackSerial']),\n trackExpiration: readBoolean(data['wms.trackExpiration']),\n reorderPoint: readNumber(data['wms.reorderPoint']) ?? 0,\n safetyStock: readNumber(data['wms.safetyStock']) ?? 0,\n }\n}\n\nfunction buildDisplayState(data: RecordLike): RecordLike {\n const wms = asRecord(data._wms)\n const inventoryProfile = asRecord(wms?.inventoryProfile)\n const fallbackDefaultUom = readString(data.defaultUnit).trim()\n\n return {\n ...data,\n 'wms.manageInventory': Boolean(inventoryProfile),\n 'wms.defaultUom':\n readString(inventoryProfile?.defaultUom).trim() ||\n fallbackDefaultUom,\n 'wms.defaultStrategy':\n readString(inventoryProfile?.defaultStrategy).trim() || 'fifo',\n 'wms.trackLot': readBoolean(inventoryProfile?.trackLot),\n 'wms.trackSerial': readBoolean(inventoryProfile?.trackSerial),\n 'wms.trackExpiration': readBoolean(inventoryProfile?.trackExpiration),\n 'wms.reorderPoint':\n readNumber(inventoryProfile?.reorderPoint) ?? 0,\n 'wms.safetyStock':\n readNumber(inventoryProfile?.safetyStock) ?? 0,\n }\n}\n\nconst visibleWhenManaged = {\n field: 'wms.manageInventory',\n operator: 'eq' as const,\n value: true,\n}\n\nconst widget: InjectionFieldWidget = {\n metadata: {\n id: 'wms.injection.catalog-inventory-profile',\n priority: 120,\n features: ['wms.manage_inventory'],\n },\n fields: [\n {\n id: 'wms.manageInventory',\n label: 'Manage inventory with WMS',\n labelKey: 'wms.widgets.catalog.inventoryProfile.manageInventory',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n },\n {\n id: 'wms.defaultUom',\n label: 'Default UOM',\n labelKey: 'wms.widgets.catalog.inventoryProfile.defaultUom',\n type: 'text',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.defaultStrategy',\n label: 'Rotation strategy',\n labelKey: 'wms.widgets.catalog.inventoryProfile.defaultStrategy',\n type: 'select',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n options: [\n {\n value: 'fifo',\n label: 'FIFO',\n labelKey: 'wms.widgets.catalog.inventoryProfile.strategy.fifo',\n },\n {\n value: 'lifo',\n label: 'LIFO',\n labelKey: 'wms.widgets.catalog.inventoryProfile.strategy.lifo',\n },\n {\n value: 'fefo',\n label: 'FEFO',\n labelKey: 'wms.widgets.catalog.inventoryProfile.strategy.fefo',\n },\n ],\n },\n {\n id: 'wms.trackLot',\n label: 'Track lots',\n labelKey: 'wms.widgets.catalog.inventoryProfile.trackLot',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.trackSerial',\n label: 'Track serials',\n labelKey: 'wms.widgets.catalog.inventoryProfile.trackSerial',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.trackExpiration',\n label: 'Track expiration',\n labelKey: 'wms.widgets.catalog.inventoryProfile.trackExpiration',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.reorderPoint',\n label: 'Reorder point',\n labelKey: 'wms.widgets.catalog.inventoryProfile.reorderPoint',\n type: 'number',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.safetyStock',\n label: 'Safety stock',\n labelKey: 'wms.widgets.catalog.inventoryProfile.safetyStock',\n type: 'number',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n ],\n eventHandlers: {\n async transformDisplayData(data) {\n const source = asRecord(data)\n if (!source) return data\n return buildDisplayState(source) as typeof data\n },\n async onBeforeSave(data) {\n const source = asRecord(data)\n if (!source) return { ok: true }\n\n const intent = buildIntent(source)\n if (!intent.manageInventory) {\n return {\n ok: true,\n requestHeaders: {\n [WMS_CATALOG_PROFILE_HEADER]:\n encodeCatalogInventoryProfileIntent(intent),\n },\n }\n }\n\n const fieldErrors: Record<string, string> = {}\n if (!intent.defaultUom) {\n fieldErrors['wms.defaultUom'] =\n 'wms.widgets.catalog.inventoryProfile.errors.defaultUomRequired'\n }\n if (!intent.defaultStrategy) {\n fieldErrors['wms.defaultStrategy'] =\n 'wms.widgets.catalog.inventoryProfile.errors.strategyRequired'\n }\n if (\n intent.trackExpiration &&\n intent.defaultStrategy &&\n intent.defaultStrategy !== 'fefo'\n ) {\n fieldErrors['wms.defaultStrategy'] =\n 'wms.widgets.catalog.inventoryProfile.errors.fefoRequired'\n }\n if (\n typeof intent.reorderPoint === 'number' &&\n intent.reorderPoint < 0\n ) {\n fieldErrors['wms.reorderPoint'] =\n 'wms.widgets.catalog.inventoryProfile.errors.nonNegative'\n }\n if (\n typeof intent.safetyStock === 'number' &&\n intent.safetyStock < 0\n ) {\n fieldErrors['wms.safetyStock'] =\n 'wms.widgets.catalog.inventoryProfile.errors.nonNegative'\n }\n\n if (Object.keys(fieldErrors).length > 0) {\n return {\n ok: false,\n fieldErrors,\n }\n }\n\n return {\n ok: true,\n requestHeaders: {\n [WMS_CATALOG_PROFILE_HEADER]:\n encodeCatalogInventoryProfileIntent(intent),\n },\n }\n },\n },\n}\n\nexport default widget\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAIP,SAAS,SAAS,OAAmC;AACnD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,SAAO;AACT;AAEA,SAAS,WAAW,OAAwB;AAC1C,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEA,SAAS,YAAY,OAAyB;AAC5C,SAAO,UAAU;AACnB;AAEA,SAAS,WAAW,OAAoC;AACtD,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,YAAY,
|
|
4
|
+
"sourcesContent": ["import type { InjectionContext, InjectionFieldWidget } from '@open-mercato/shared/modules/widgets/injection'\nimport {\n encodeCatalogInventoryProfileIntent,\n WMS_CATALOG_PROFILE_HEADER,\n type CatalogInventoryProfileIntent,\n} from '../../../lib/catalogInventoryProfileIntent'\n\ntype RecordLike = Record<string, unknown>\n\nfunction asRecord(value: unknown): RecordLike | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n return value as RecordLike\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction readBoolean(value: unknown): boolean {\n return value === true\n}\n\nfunction readNumber(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n return undefined\n}\n\nfunction buildIntent(data: RecordLike, sourceEntityId: string): CatalogInventoryProfileIntent {\n return {\n sourceEntityId,\n manageInventory: readBoolean(data['wms.manageInventory']),\n defaultUom: readString(data['wms.defaultUom']).trim() || null,\n defaultStrategy: (() => {\n const strategy = readString(data['wms.defaultStrategy']).trim()\n return strategy === 'fifo' || strategy === 'lifo' || strategy === 'fefo'\n ? strategy\n : undefined\n })(),\n trackLot: readBoolean(data['wms.trackLot']),\n trackSerial: readBoolean(data['wms.trackSerial']),\n trackExpiration: readBoolean(data['wms.trackExpiration']),\n reorderPoint: readNumber(data['wms.reorderPoint']) ?? 0,\n safetyStock: readNumber(data['wms.safetyStock']) ?? 0,\n }\n}\n\nfunction hasLoadedInventoryProfile(data: RecordLike): boolean {\n return Boolean(asRecord(asRecord(data._wms)?.inventoryProfile))\n}\n\nfunction buildDisplayState(data: RecordLike): RecordLike {\n const wms = asRecord(data._wms)\n const inventoryProfile = asRecord(wms?.inventoryProfile)\n const fallbackDefaultUom = readString(data.defaultUnit).trim()\n\n return {\n ...data,\n 'wms.manageInventory': Boolean(inventoryProfile),\n 'wms.defaultUom':\n readString(inventoryProfile?.defaultUom).trim() ||\n fallbackDefaultUom,\n 'wms.defaultStrategy':\n readString(inventoryProfile?.defaultStrategy).trim() || 'fifo',\n 'wms.trackLot': readBoolean(inventoryProfile?.trackLot),\n 'wms.trackSerial': readBoolean(inventoryProfile?.trackSerial),\n 'wms.trackExpiration': readBoolean(inventoryProfile?.trackExpiration),\n 'wms.reorderPoint':\n readNumber(inventoryProfile?.reorderPoint) ?? 0,\n 'wms.safetyStock':\n readNumber(inventoryProfile?.safetyStock) ?? 0,\n }\n}\n\nconst visibleWhenManaged = {\n field: 'wms.manageInventory',\n operator: 'eq' as const,\n value: true,\n}\n\nconst widget: InjectionFieldWidget = {\n metadata: {\n id: 'wms.injection.catalog-inventory-profile',\n priority: 120,\n features: ['wms.manage_inventory'],\n },\n fields: [\n {\n id: 'wms.manageInventory',\n label: 'Manage inventory with WMS',\n labelKey: 'wms.widgets.catalog.inventoryProfile.manageInventory',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n },\n {\n id: 'wms.defaultUom',\n label: 'Default UOM',\n labelKey: 'wms.widgets.catalog.inventoryProfile.defaultUom',\n type: 'text',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.defaultStrategy',\n label: 'Rotation strategy',\n labelKey: 'wms.widgets.catalog.inventoryProfile.defaultStrategy',\n type: 'select',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n options: [\n {\n value: 'fifo',\n label: 'FIFO',\n labelKey: 'wms.widgets.catalog.inventoryProfile.strategy.fifo',\n },\n {\n value: 'lifo',\n label: 'LIFO',\n labelKey: 'wms.widgets.catalog.inventoryProfile.strategy.lifo',\n },\n {\n value: 'fefo',\n label: 'FEFO',\n labelKey: 'wms.widgets.catalog.inventoryProfile.strategy.fefo',\n },\n ],\n },\n {\n id: 'wms.trackLot',\n label: 'Track lots',\n labelKey: 'wms.widgets.catalog.inventoryProfile.trackLot',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.trackSerial',\n label: 'Track serials',\n labelKey: 'wms.widgets.catalog.inventoryProfile.trackSerial',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.trackExpiration',\n label: 'Track expiration',\n labelKey: 'wms.widgets.catalog.inventoryProfile.trackExpiration',\n type: 'boolean',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.reorderPoint',\n label: 'Reorder point',\n labelKey: 'wms.widgets.catalog.inventoryProfile.reorderPoint',\n type: 'number',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n {\n id: 'wms.safetyStock',\n label: 'Safety stock',\n labelKey: 'wms.widgets.catalog.inventoryProfile.safetyStock',\n type: 'number',\n group: 'wms.inventoryProfile',\n visibleWhen: visibleWhenManaged,\n },\n ],\n eventHandlers: {\n async transformDisplayData(data) {\n const source = asRecord(data)\n if (!source) return data\n return buildDisplayState(source) as typeof data\n },\n async onBeforeSave(data, context: InjectionContext) {\n const source = asRecord(data)\n if (!source) return { ok: true }\n\n const sourceEntityId = readString(context?.entityId)\n const intent = buildIntent(source, sourceEntityId)\n if (!intent.manageInventory) {\n if (source['wms.manageInventory'] !== false || !hasLoadedInventoryProfile(source)) {\n return { ok: true }\n }\n return {\n ok: true,\n requestHeaders: {\n [WMS_CATALOG_PROFILE_HEADER]:\n encodeCatalogInventoryProfileIntent(intent),\n },\n }\n }\n\n const fieldErrors: Record<string, string> = {}\n if (!intent.defaultUom) {\n fieldErrors['wms.defaultUom'] =\n 'wms.widgets.catalog.inventoryProfile.errors.defaultUomRequired'\n }\n if (!intent.defaultStrategy) {\n fieldErrors['wms.defaultStrategy'] =\n 'wms.widgets.catalog.inventoryProfile.errors.strategyRequired'\n }\n if (\n intent.trackExpiration &&\n intent.defaultStrategy &&\n intent.defaultStrategy !== 'fefo'\n ) {\n fieldErrors['wms.defaultStrategy'] =\n 'wms.widgets.catalog.inventoryProfile.errors.fefoRequired'\n }\n if (\n typeof intent.reorderPoint === 'number' &&\n intent.reorderPoint < 0\n ) {\n fieldErrors['wms.reorderPoint'] =\n 'wms.widgets.catalog.inventoryProfile.errors.nonNegative'\n }\n if (\n typeof intent.safetyStock === 'number' &&\n intent.safetyStock < 0\n ) {\n fieldErrors['wms.safetyStock'] =\n 'wms.widgets.catalog.inventoryProfile.errors.nonNegative'\n }\n\n if (Object.keys(fieldErrors).length > 0) {\n return {\n ok: false,\n fieldErrors,\n }\n }\n\n return {\n ok: true,\n requestHeaders: {\n [WMS_CATALOG_PROFILE_HEADER]:\n encodeCatalogInventoryProfileIntent(intent),\n },\n }\n },\n },\n}\n\nexport default widget\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.9.1-develop.
|
|
3
|
+
"version": "0.9.1-develop.7515.1.e2712c8b00",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -264,16 +264,16 @@
|
|
|
264
264
|
"zod": "^4.4.3"
|
|
265
265
|
},
|
|
266
266
|
"peerDependencies": {
|
|
267
|
-
"@open-mercato/ai-assistant": "0.9.1-develop.
|
|
268
|
-
"@open-mercato/shared": "0.9.1-develop.
|
|
269
|
-
"@open-mercato/ui": "0.9.1-develop.
|
|
267
|
+
"@open-mercato/ai-assistant": "0.9.1-develop.7515.1.e2712c8b00",
|
|
268
|
+
"@open-mercato/shared": "0.9.1-develop.7515.1.e2712c8b00",
|
|
269
|
+
"@open-mercato/ui": "0.9.1-develop.7515.1.e2712c8b00",
|
|
270
270
|
"react": "^19.0.0",
|
|
271
271
|
"react-dom": "^19.0.0"
|
|
272
272
|
},
|
|
273
273
|
"devDependencies": {
|
|
274
|
-
"@open-mercato/ai-assistant": "0.9.1-develop.
|
|
275
|
-
"@open-mercato/shared": "0.9.1-develop.
|
|
276
|
-
"@open-mercato/ui": "0.9.1-develop.
|
|
274
|
+
"@open-mercato/ai-assistant": "0.9.1-develop.7515.1.e2712c8b00",
|
|
275
|
+
"@open-mercato/shared": "0.9.1-develop.7515.1.e2712c8b00",
|
|
276
|
+
"@open-mercato/ui": "0.9.1-develop.7515.1.e2712c8b00",
|
|
277
277
|
"@testing-library/dom": "^10.4.1",
|
|
278
278
|
"@testing-library/jest-dom": "^7.0.1",
|
|
279
279
|
"@testing-library/react": "^16.3.3",
|
|
@@ -201,7 +201,7 @@ export function AuditLogsActions({
|
|
|
201
201
|
const canRedo = showRedo && isRedoCandidate(item)
|
|
202
202
|
if (!canUndo && !showRedo) return null
|
|
203
203
|
return (
|
|
204
|
-
<div className="flex justify-end gap-1">
|
|
204
|
+
<div className="flex justify-end gap-1" data-actions-cell>
|
|
205
205
|
{canUndo ? (
|
|
206
206
|
<Button
|
|
207
207
|
variant="ghost"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client"
|
|
2
|
-
import { useState } from 'react'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
3
|
import Link from 'next/link'
|
|
4
4
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@open-mercato/ui/primitives/card'
|
|
5
5
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
@@ -13,6 +13,11 @@ export default function ResetPage() {
|
|
|
13
13
|
const [submitting, setSubmitting] = useState(false)
|
|
14
14
|
const [error, setError] = useState<string | null>(null)
|
|
15
15
|
const [fieldError, setFieldError] = useState<string | null>(null)
|
|
16
|
+
const [clientReady, setClientReady] = useState(false)
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
setClientReady(true)
|
|
20
|
+
}, [])
|
|
16
21
|
|
|
17
22
|
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
|
|
18
23
|
e.preventDefault()
|
|
@@ -55,14 +60,14 @@ export default function ResetPage() {
|
|
|
55
60
|
</Link>
|
|
56
61
|
</div>
|
|
57
62
|
) : (
|
|
58
|
-
<form className="grid gap-3" onSubmit={onSubmit} noValidate>
|
|
63
|
+
<form className="grid gap-3" method="post" onSubmit={onSubmit} noValidate data-auth-ready={clientReady ? '1' : '0'}>
|
|
59
64
|
{error && <div className="text-sm text-status-error-text">{error}</div>}
|
|
60
65
|
<div className="grid gap-1">
|
|
61
66
|
<Label htmlFor="email">{t('auth.email')}</Label>
|
|
62
67
|
<EmailInput id="email" name="email" required aria-invalid={!!fieldError} aria-describedby={fieldError ? 'email-error' : undefined} />
|
|
63
68
|
{fieldError && <p id="email-error" className="text-sm text-status-error-text">{fieldError}</p>}
|
|
64
69
|
</div>
|
|
65
|
-
<Button type="submit" className="mt-2 w-full" disabled={submitting}>
|
|
70
|
+
<Button type="submit" className="mt-2 w-full" disabled={submitting || !clientReady}>
|
|
66
71
|
{submitting ? '...' : t('auth.sendResetLink')}
|
|
67
72
|
</Button>
|
|
68
73
|
<div className="text-center">
|
|
@@ -5,6 +5,7 @@ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
|
5
5
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
6
6
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
7
7
|
import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
8
|
+
import { isUniqueViolation } from '@open-mercato/shared/lib/db/pg-errors'
|
|
8
9
|
import { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
|
|
9
10
|
import { BusinessRule } from '../../data/entities'
|
|
10
11
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
@@ -284,6 +285,15 @@ export async function POST(req: Request) {
|
|
|
284
285
|
await em.persist(rule).flush()
|
|
285
286
|
await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)
|
|
286
287
|
} catch (error) {
|
|
288
|
+
if (isUniqueViolation(error)) {
|
|
289
|
+
return NextResponse.json(
|
|
290
|
+
{
|
|
291
|
+
error: t('business_rules.errors.duplicateRuleId', { ruleId: parsed.data.ruleId }),
|
|
292
|
+
fieldErrors: { ruleId: t('business_rules.errors.duplicateRuleId', { ruleId: parsed.data.ruleId }) },
|
|
293
|
+
},
|
|
294
|
+
{ status: 409 },
|
|
295
|
+
)
|
|
296
|
+
}
|
|
287
297
|
logger.error('Failed to persist new rule', { err: error })
|
|
288
298
|
return NextResponse.json(
|
|
289
299
|
{ error: t('business_rules.errors.createFailed') },
|
|
@@ -482,6 +492,7 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
482
492
|
errors: [
|
|
483
493
|
{ status: 400, description: 'Invalid payload', schema: errorResponseSchema },
|
|
484
494
|
{ status: 401, description: 'Unauthorized', schema: errorResponseSchema },
|
|
495
|
+
{ status: 409, description: 'A rule with this Rule ID already exists for the tenant', schema: errorResponseSchema },
|
|
485
496
|
],
|
|
486
497
|
},
|
|
487
498
|
PUT: {
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"business_rules.components.conditionRow.value.placeholder": "z.B. \"ACTIVE\" oder [\"A\",\"B\"]",
|
|
97
97
|
"business_rules.confirm.delete": "Sind Sie sicher, dass Sie die Regel \"{{name}}\" löschen möchten?",
|
|
98
98
|
"business_rules.errors.createFailed": "Geschäftsregel konnte nicht erstellt werden",
|
|
99
|
+
"business_rules.errors.duplicateRuleId": "Für diesen Mandanten existiert bereits eine Regel mit der Regel-ID \"{{ruleId}}\". Wählen Sie eine andere Regel-ID.",
|
|
99
100
|
"business_rules.errors.executionFailed": "Regelausführung fehlgeschlagen",
|
|
100
101
|
"business_rules.errors.fetchFailed": "Regel konnte nicht abgerufen werden",
|
|
101
102
|
"business_rules.errors.invalidCondition": "Ungültige Bedingung",
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"business_rules.components.conditionRow.value.placeholder": "e.g., \"ACTIVE\" or [\"A\",\"B\"]",
|
|
97
97
|
"business_rules.confirm.delete": "Are you sure you want to delete the rule \"{{name}}\"?",
|
|
98
98
|
"business_rules.errors.createFailed": "Failed to create business rule",
|
|
99
|
+
"business_rules.errors.duplicateRuleId": "A rule with Rule ID \"{{ruleId}}\" already exists for this tenant. Choose a different Rule ID.",
|
|
99
100
|
"business_rules.errors.executionFailed": "Rule execution failed",
|
|
100
101
|
"business_rules.errors.fetchFailed": "Failed to fetch rule",
|
|
101
102
|
"business_rules.errors.invalidCondition": "Invalid condition",
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"business_rules.components.conditionRow.value.placeholder": "ej. \"ACTIVE\" o [\"A\",\"B\"]",
|
|
97
97
|
"business_rules.confirm.delete": "¿Está seguro de que desea eliminar la regla \"{{name}}\"?",
|
|
98
98
|
"business_rules.errors.createFailed": "Error al crear la regla de negocio",
|
|
99
|
+
"business_rules.errors.duplicateRuleId": "Ya existe una regla con el ID de regla \"{{ruleId}}\" para este inquilino. Elija un ID de regla diferente.",
|
|
99
100
|
"business_rules.errors.executionFailed": "Falló la ejecución de la regla",
|
|
100
101
|
"business_rules.errors.fetchFailed": "Error al obtener la regla",
|
|
101
102
|
"business_rules.errors.invalidCondition": "Condición inválida",
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"business_rules.components.conditionRow.value.placeholder": "예: \"ACTIVE\" 또는 [\"A\",\"B\"]",
|
|
97
97
|
"business_rules.confirm.delete": "규칙 \"{{name}}\"을(를) 삭제하시겠습니까?",
|
|
98
98
|
"business_rules.errors.createFailed": "비즈니스 규칙 생성에 실패했습니다",
|
|
99
|
+
"business_rules.errors.duplicateRuleId": "이 테넌트에는 규칙 ID \"{{ruleId}}\"를 사용하는 규칙이 이미 있습니다. 다른 규칙 ID를 선택하세요.",
|
|
99
100
|
"business_rules.errors.executionFailed": "규칙 실행에 실패했습니다",
|
|
100
101
|
"business_rules.errors.fetchFailed": "규칙을 가져오지 못했습니다",
|
|
101
102
|
"business_rules.errors.invalidCondition": "잘못된 조건입니다",
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"business_rules.components.conditionRow.value.placeholder": "np. \"ACTIVE\" lub [\"A\",\"B\"]",
|
|
97
97
|
"business_rules.confirm.delete": "Czy na pewno chcesz usunąć regułę \"{{name}}\"?",
|
|
98
98
|
"business_rules.errors.createFailed": "Nie udało się utworzyć reguły biznesowej",
|
|
99
|
+
"business_rules.errors.duplicateRuleId": "Reguła z identyfikatorem \"{{ruleId}}\" już istnieje dla tego najemcy. Wybierz inny identyfikator reguły.",
|
|
99
100
|
"business_rules.errors.executionFailed": "Wykonanie reguły nie powiodło się",
|
|
100
101
|
"business_rules.errors.fetchFailed": "Nie udało się pobrać reguły",
|
|
101
102
|
"business_rules.errors.invalidCondition": "Nieprawidłowy warunek",
|
|
@@ -120,6 +120,7 @@ import {
|
|
|
120
120
|
toPositiveNumberOrNull,
|
|
121
121
|
toIntegerInRangeOrDefault,
|
|
122
122
|
normalizeProductConversionInputs,
|
|
123
|
+
pickEnrichmentNamespaces,
|
|
123
124
|
type ProductUnitConversionInput,
|
|
124
125
|
} from "@open-mercato/core/modules/catalog/components/products/productFormUtils";
|
|
125
126
|
import {
|
|
@@ -773,7 +774,11 @@ export default function EditCatalogProductPage({
|
|
|
773
774
|
: null,
|
|
774
775
|
};
|
|
775
776
|
if (!cancelled) {
|
|
776
|
-
setInitialValues({
|
|
777
|
+
setInitialValues({
|
|
778
|
+
...initial,
|
|
779
|
+
...customValues,
|
|
780
|
+
...pickEnrichmentNamespaces(record),
|
|
781
|
+
});
|
|
777
782
|
setCategorizeOptions({
|
|
778
783
|
categories: categoryOptions,
|
|
779
784
|
channels: channelOptionEntries,
|
|
@@ -1394,6 +1399,19 @@ export default function EditCatalogProductPage({
|
|
|
1394
1399
|
typeof updateResult.result?.updatedAt === "string"
|
|
1395
1400
|
? updateResult.result.updatedAt
|
|
1396
1401
|
: null;
|
|
1402
|
+
// The update route does not echo response enrichments (`_wms`, …), so re-read
|
|
1403
|
+
// them when the record carries any — otherwise CrudForm's post-save
|
|
1404
|
+
// `transformDisplayData` resync restores the injected fields' pre-save values
|
|
1405
|
+
// and the next save writes them back (#6142).
|
|
1406
|
+
let refreshedEnrichments: Record<string, unknown> = {};
|
|
1407
|
+
if (Object.keys(pickEnrichmentNamespaces(formValues as Record<string, unknown>)).length > 0) {
|
|
1408
|
+
const refreshedProductRes = await apiCall<ProductResponse>(
|
|
1409
|
+
`/api/catalog/products?id=${encodeURIComponent(productId)}&page=1&pageSize=1&withDeleted=false`,
|
|
1410
|
+
);
|
|
1411
|
+
refreshedEnrichments = pickEnrichmentNamespaces(
|
|
1412
|
+
refreshedProductRes.ok ? refreshedProductRes.result?.items?.[0] : undefined,
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1397
1415
|
// Merge the just-submitted `values` back into `initialValues` too, not only
|
|
1398
1416
|
// `updatedAt` — CrudForm re-syncs its visible fields from `initialValues`
|
|
1399
1417
|
// whenever that prop's identity changes, so leaving the other fields at
|
|
@@ -1404,6 +1422,7 @@ export default function EditCatalogProductPage({
|
|
|
1404
1422
|
? {
|
|
1405
1423
|
...prev,
|
|
1406
1424
|
...values,
|
|
1425
|
+
...refreshedEnrichments,
|
|
1407
1426
|
updatedAt: refreshedUpdatedAt ?? prev.updatedAt,
|
|
1408
1427
|
}
|
|
1409
1428
|
: prev,
|
package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
normalizeTaxRateSummary,
|
|
35
35
|
mergeTaxRateSummaries,
|
|
36
36
|
} from '@open-mercato/core/modules/catalog/components/products/productForm'
|
|
37
|
-
import { parseNumericInput } from '@open-mercato/core/modules/catalog/components/products/productFormUtils'
|
|
37
|
+
import { parseNumericInput, pickEnrichmentNamespaces } from '@open-mercato/core/modules/catalog/components/products/productFormUtils'
|
|
38
38
|
import {
|
|
39
39
|
VariantBasicsSection,
|
|
40
40
|
VariantOptionValuesSection,
|
|
@@ -385,6 +385,7 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
|
|
|
385
385
|
? record.updated_at
|
|
386
386
|
: null,
|
|
387
387
|
...customDefaults,
|
|
388
|
+
...pickEnrichmentNamespaces(record),
|
|
388
389
|
})
|
|
389
390
|
}
|
|
390
391
|
} catch (err) {
|
|
@@ -385,6 +385,7 @@ export default function CreateCatalogProductPage() {
|
|
|
385
385
|
backHref="/backend/catalog/products"
|
|
386
386
|
fields={[]}
|
|
387
387
|
groups={groups}
|
|
388
|
+
entityId={E.catalog.catalog_product}
|
|
388
389
|
injectionSpotId={extensionPoints.hosts.productForm.spotId}
|
|
389
390
|
initialValues={
|
|
390
391
|
initialValuesRef.current ?? createInitialProductFormValues()
|
|
@@ -11,6 +11,15 @@ export function toTrimmedOrNull(value: unknown): string | null {
|
|
|
11
11
|
return trimmed.length ? trimmed : null;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export function pickEnrichmentNamespaces(record: Record<string, unknown> | null | undefined): Record<string, unknown> {
|
|
15
|
+
const namespaces: Record<string, unknown> = {};
|
|
16
|
+
if (!record) return namespaces;
|
|
17
|
+
for (const [key, value] of Object.entries(record)) {
|
|
18
|
+
if (key.length > 1 && key.startsWith("_")) namespaces[key] = value;
|
|
19
|
+
}
|
|
20
|
+
return namespaces;
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
export function parseNumericInput(value: unknown): number {
|
|
15
24
|
if (typeof value === "number") return value;
|
|
16
25
|
if (typeof value === "string") {
|