@open-mercato/core 0.4.7-develop-0a657b411f → 0.4.7-develop-e249d3e7d0
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/dist/generated/entities/carrier_shipment/index.js +37 -0
- package/dist/generated/entities/carrier_shipment/index.js.map +7 -0
- package/dist/generated/entities/gateway_transaction/index.js +47 -0
- package/dist/generated/entities/gateway_transaction/index.js.map +7 -0
- package/dist/generated/entities/webhook_processed_event/index.js +17 -0
- package/dist/generated/entities/webhook_processed_event/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +10 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +6 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +14 -5
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.meta.js +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +1 -1
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +37 -12
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/directory/api/get/tenants/lookup.js +1 -0
- package/dist/modules/directory/api/get/tenants/lookup.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +38 -11
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/logs/route.js +52 -26
- package/dist/modules/integrations/api/logs/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +37 -7
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/api/umes-read.js +121 -0
- package/dist/modules/integrations/api/umes-read.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js +715 -183
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +30 -9
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/detail-page-widgets.js +46 -0
- package/dist/modules/integrations/backend/integrations/detail-page-widgets.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.js +78 -62
- package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/page.meta.js +2 -2
- package/dist/modules/integrations/backend/integrations/page.meta.js.map +1 -1
- package/dist/modules/integrations/setup.js +2 -2
- package/dist/modules/integrations/setup.js.map +2 -2
- package/dist/modules/payment_gateways/acl.js +12 -0
- package/dist/modules/payment_gateways/acl.js.map +7 -0
- package/dist/modules/payment_gateways/api/cancel/route.js +55 -0
- package/dist/modules/payment_gateways/api/cancel/route.js.map +7 -0
- package/dist/modules/payment_gateways/api/capture/route.js +55 -0
- package/dist/modules/payment_gateways/api/capture/route.js.map +7 -0
- package/dist/modules/payment_gateways/api/interceptors.js +24 -0
- package/dist/modules/payment_gateways/api/interceptors.js.map +7 -0
- package/dist/modules/payment_gateways/api/openapi.js +5 -0
- package/dist/modules/payment_gateways/api/openapi.js.map +7 -0
- package/dist/modules/payment_gateways/api/refund/route.js +56 -0
- package/dist/modules/payment_gateways/api/refund/route.js.map +7 -0
- package/dist/modules/payment_gateways/api/sessions/route.js +74 -0
- package/dist/modules/payment_gateways/api/sessions/route.js.map +7 -0
- package/dist/modules/payment_gateways/api/status/route.js +66 -0
- package/dist/modules/payment_gateways/api/status/route.js.map +7 -0
- package/dist/modules/payment_gateways/api/transactions/[id]/route.js +118 -0
- package/dist/modules/payment_gateways/api/transactions/[id]/route.js.map +7 -0
- package/dist/modules/payment_gateways/api/transactions/route.js +113 -0
- package/dist/modules/payment_gateways/api/transactions/route.js.map +7 -0
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +136 -0
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +7 -0
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +496 -0
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +7 -0
- package/dist/modules/payment_gateways/backend/payment-gateways/page.meta.js +23 -0
- package/dist/modules/payment_gateways/backend/payment-gateways/page.meta.js.map +7 -0
- package/dist/modules/payment_gateways/data/enrichers.js +5 -0
- package/dist/modules/payment_gateways/data/enrichers.js.map +7 -0
- package/dist/modules/payment_gateways/data/entities.js +131 -0
- package/dist/modules/payment_gateways/data/entities.js.map +7 -0
- package/dist/modules/payment_gateways/data/validators.js +57 -0
- package/dist/modules/payment_gateways/data/validators.js.map +7 -0
- package/dist/modules/payment_gateways/di.js +16 -0
- package/dist/modules/payment_gateways/di.js.map +7 -0
- package/dist/modules/payment_gateways/events.js +21 -0
- package/dist/modules/payment_gateways/events.js.map +7 -0
- package/dist/modules/payment_gateways/i18n/en.js +6 -0
- package/dist/modules/payment_gateways/i18n/en.js.map +7 -0
- package/dist/modules/payment_gateways/i18n/pl.js +6 -0
- package/dist/modules/payment_gateways/i18n/pl.js.map +7 -0
- package/dist/modules/payment_gateways/index.js +9 -0
- package/dist/modules/payment_gateways/index.js.map +7 -0
- package/dist/modules/payment_gateways/lib/gateway-service.js +378 -0
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +7 -0
- package/dist/modules/payment_gateways/lib/queue.js +17 -0
- package/dist/modules/payment_gateways/lib/queue.js.map +7 -0
- package/dist/modules/payment_gateways/lib/status-machine.js +29 -0
- package/dist/modules/payment_gateways/lib/status-machine.js.map +7 -0
- package/dist/modules/payment_gateways/lib/webhook-processor.js +88 -0
- package/dist/modules/payment_gateways/lib/webhook-processor.js.map +7 -0
- package/dist/modules/payment_gateways/lib/webhook-utils.js +42 -0
- package/dist/modules/payment_gateways/lib/webhook-utils.js.map +7 -0
- package/dist/modules/payment_gateways/migrations/Migration20260305122155.js +19 -0
- package/dist/modules/payment_gateways/migrations/Migration20260305122155.js.map +7 -0
- package/dist/modules/payment_gateways/setup.js +13 -0
- package/dist/modules/payment_gateways/setup.js.map +7 -0
- package/dist/modules/payment_gateways/widgets/injection-table.js +7 -0
- package/dist/modules/payment_gateways/widgets/injection-table.js.map +7 -0
- package/dist/modules/payment_gateways/workers/status-poller.js +44 -0
- package/dist/modules/payment_gateways/workers/status-poller.js.map +7 -0
- package/dist/modules/payment_gateways/workers/webhook-processor.js +20 -0
- package/dist/modules/payment_gateways/workers/webhook-processor.js.map +7 -0
- package/dist/modules/sales/data/enrichers.js +72 -0
- package/dist/modules/sales/data/enrichers.js.map +7 -0
- package/dist/modules/sales/lib/makeSalesLineRoute.js +3 -0
- package/dist/modules/sales/lib/makeSalesLineRoute.js.map +2 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-config-field/widget.js +29 -0
- package/dist/modules/sales/widgets/injection/payment-gateway-config-field/widget.js.map +7 -0
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +23 -0
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +7 -0
- package/dist/modules/sales/widgets/injection-table.js +13 -1
- package/dist/modules/sales/widgets/injection-table.js.map +2 -2
- package/dist/modules/shipping_carriers/acl.js +10 -0
- package/dist/modules/shipping_carriers/acl.js.map +7 -0
- package/dist/modules/shipping_carriers/api/cancel/route.js +55 -0
- package/dist/modules/shipping_carriers/api/cancel/route.js.map +7 -0
- package/dist/modules/shipping_carriers/api/interceptors.js +21 -0
- package/dist/modules/shipping_carriers/api/interceptors.js.map +7 -0
- package/dist/modules/shipping_carriers/api/openapi.js +5 -0
- package/dist/modules/shipping_carriers/api/openapi.js.map +7 -0
- package/dist/modules/shipping_carriers/api/rates/route.js +55 -0
- package/dist/modules/shipping_carriers/api/rates/route.js.map +7 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js +61 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +7 -0
- package/dist/modules/shipping_carriers/api/tracking/route.js +58 -0
- package/dist/modules/shipping_carriers/api/tracking/route.js.map +7 -0
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +119 -0
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +7 -0
- package/dist/modules/shipping_carriers/data/enrichers.js +82 -0
- package/dist/modules/shipping_carriers/data/enrichers.js.map +7 -0
- package/dist/modules/shipping_carriers/data/entities.js +80 -0
- package/dist/modules/shipping_carriers/data/entities.js.map +7 -0
- package/dist/modules/shipping_carriers/data/validators.js +49 -0
- package/dist/modules/shipping_carriers/data/validators.js.map +7 -0
- package/dist/modules/shipping_carriers/di.js +15 -0
- package/dist/modules/shipping_carriers/di.js.map +7 -0
- package/dist/modules/shipping_carriers/events.js +19 -0
- package/dist/modules/shipping_carriers/events.js.map +7 -0
- package/dist/modules/shipping_carriers/i18n/en.js +11 -0
- package/dist/modules/shipping_carriers/i18n/en.js.map +7 -0
- package/dist/modules/shipping_carriers/i18n/pl.js +11 -0
- package/dist/modules/shipping_carriers/i18n/pl.js.map +7 -0
- package/dist/modules/shipping_carriers/index.js +9 -0
- package/dist/modules/shipping_carriers/index.js.map +7 -0
- package/dist/modules/shipping_carriers/lib/adapter-registry.js +29 -0
- package/dist/modules/shipping_carriers/lib/adapter-registry.js.map +7 -0
- package/dist/modules/shipping_carriers/lib/adapter.js +1 -0
- package/dist/modules/shipping_carriers/lib/adapter.js.map +7 -0
- package/dist/modules/shipping_carriers/lib/queue.js +17 -0
- package/dist/modules/shipping_carriers/lib/queue.js.map +7 -0
- package/dist/modules/shipping_carriers/lib/shipping-service.js +155 -0
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +7 -0
- package/dist/modules/shipping_carriers/lib/status-sync.js +37 -0
- package/dist/modules/shipping_carriers/lib/status-sync.js.map +7 -0
- package/dist/modules/shipping_carriers/migrations/Migration20260305170000.js +16 -0
- package/dist/modules/shipping_carriers/migrations/Migration20260305170000.js.map +7 -0
- package/dist/modules/shipping_carriers/setup.js +13 -0
- package/dist/modules/shipping_carriers/setup.js.map +7 -0
- package/dist/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.js +25 -0
- package/dist/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.js.map +7 -0
- package/dist/modules/shipping_carriers/widgets/injection/tracking-column/widget.js +23 -0
- package/dist/modules/shipping_carriers/widgets/injection/tracking-column/widget.js.map +7 -0
- package/dist/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.js +40 -0
- package/dist/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.js.map +7 -0
- package/dist/modules/shipping_carriers/widgets/injection-table.js +24 -0
- package/dist/modules/shipping_carriers/widgets/injection-table.js.map +7 -0
- package/dist/modules/shipping_carriers/workers/status-poller.js +21 -0
- package/dist/modules/shipping_carriers/workers/status-poller.js.map +7 -0
- package/dist/modules/shipping_carriers/workers/webhook-processor.js +54 -0
- package/dist/modules/shipping_carriers/workers/webhook-processor.js.map +7 -0
- package/dist/modules/translations/api/get/locales.js +1 -0
- package/dist/modules/translations/api/get/locales.js.map +2 -2
- package/dist/modules/translations/api/put/locales.js +1 -0
- package/dist/modules/translations/api/put/locales.js.map +2 -2
- package/generated/entities/carrier_shipment/index.ts +17 -0
- package/generated/entities/gateway_transaction/index.ts +22 -0
- package/generated/entities/webhook_processed_event/index.ts +7 -0
- package/generated/entities.ids.generated.ts +10 -1
- package/generated/entity-fields-registry.ts +6 -0
- package/jest.config.cjs +1 -0
- package/package.json +5 -2
- package/src/modules/auth/i18n/de.json +1 -0
- package/src/modules/auth/i18n/en.json +1 -0
- package/src/modules/auth/i18n/es.json +1 -0
- package/src/modules/auth/i18n/pl.json +1 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +18 -5
- package/src/modules/data_sync/backend/data-sync/page.meta.ts +2 -2
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +50 -12
- package/src/modules/directory/api/get/tenants/lookup.ts +1 -0
- package/src/modules/integrations/AGENTS.md +31 -0
- package/src/modules/integrations/api/[id]/route.ts +38 -11
- package/src/modules/integrations/api/logs/route.ts +53 -27
- package/src/modules/integrations/api/route.ts +31 -1
- package/src/modules/integrations/api/umes-read.ts +177 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +902 -202
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +43 -9
- package/src/modules/integrations/backend/integrations/detail-page-widgets.ts +74 -0
- package/src/modules/integrations/backend/integrations/page.meta.ts +2 -2
- package/src/modules/integrations/backend/integrations/page.tsx +65 -54
- package/src/modules/integrations/i18n/de.json +15 -0
- package/src/modules/integrations/i18n/en.json +15 -0
- package/src/modules/integrations/i18n/es.json +15 -0
- package/src/modules/integrations/i18n/pl.json +15 -0
- package/src/modules/integrations/setup.ts +2 -2
- package/src/modules/payment_gateways/acl.ts +8 -0
- package/src/modules/payment_gateways/api/cancel/route.ts +56 -0
- package/src/modules/payment_gateways/api/capture/route.ts +56 -0
- package/src/modules/payment_gateways/api/interceptors.ts +22 -0
- package/src/modules/payment_gateways/api/openapi.ts +1 -0
- package/src/modules/payment_gateways/api/refund/route.ts +57 -0
- package/src/modules/payment_gateways/api/sessions/route.ts +76 -0
- package/src/modules/payment_gateways/api/status/route.ts +69 -0
- package/src/modules/payment_gateways/api/transactions/[id]/route.ts +123 -0
- package/src/modules/payment_gateways/api/transactions/route.ts +120 -0
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +161 -0
- package/src/modules/payment_gateways/backend/payment-gateways/page.meta.ts +19 -0
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +660 -0
- package/src/modules/payment_gateways/data/enrichers.ts +8 -0
- package/src/modules/payment_gateways/data/entities.ts +106 -0
- package/src/modules/payment_gateways/data/validators.ts +67 -0
- package/src/modules/payment_gateways/di.ts +26 -0
- package/src/modules/payment_gateways/events.ts +17 -0
- package/src/modules/payment_gateways/i18n/de.json +77 -0
- package/src/modules/payment_gateways/i18n/en.json +77 -0
- package/src/modules/payment_gateways/i18n/en.ts +4 -0
- package/src/modules/payment_gateways/i18n/es.json +77 -0
- package/src/modules/payment_gateways/i18n/pl.json +77 -0
- package/src/modules/payment_gateways/i18n/pl.ts +4 -0
- package/src/modules/payment_gateways/index.ts +5 -0
- package/src/modules/payment_gateways/lib/gateway-service.ts +486 -0
- package/src/modules/payment_gateways/lib/queue.ts +19 -0
- package/src/modules/payment_gateways/lib/status-machine.ts +28 -0
- package/src/modules/payment_gateways/lib/webhook-processor.ts +133 -0
- package/src/modules/payment_gateways/lib/webhook-utils.ts +52 -0
- package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +373 -0
- package/src/modules/payment_gateways/migrations/Migration20260305122155.ts +20 -0
- package/src/modules/payment_gateways/setup.ts +11 -0
- package/src/modules/payment_gateways/widgets/injection-table.ts +9 -0
- package/src/modules/payment_gateways/workers/status-poller.ts +58 -0
- package/src/modules/payment_gateways/workers/webhook-processor.ts +30 -0
- package/src/modules/sales/data/enrichers.ts +120 -0
- package/src/modules/sales/lib/makeSalesLineRoute.ts +3 -0
- package/src/modules/sales/widgets/injection/payment-gateway-config-field/widget.ts +28 -0
- package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +22 -0
- package/src/modules/sales/widgets/injection-table.ts +12 -0
- package/src/modules/shipping_carriers/acl.ts +6 -0
- package/src/modules/shipping_carriers/api/cancel/route.ts +53 -0
- package/src/modules/shipping_carriers/api/interceptors.ts +19 -0
- package/src/modules/shipping_carriers/api/openapi.ts +1 -0
- package/src/modules/shipping_carriers/api/rates/route.ts +53 -0
- package/src/modules/shipping_carriers/api/shipments/route.ts +59 -0
- package/src/modules/shipping_carriers/api/tracking/route.ts +56 -0
- package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +134 -0
- package/src/modules/shipping_carriers/data/enrichers.ts +89 -0
- package/src/modules/shipping_carriers/data/entities.ts +60 -0
- package/src/modules/shipping_carriers/data/validators.ts +48 -0
- package/src/modules/shipping_carriers/di.ts +20 -0
- package/src/modules/shipping_carriers/events.ts +16 -0
- package/src/modules/shipping_carriers/i18n/de.json +7 -0
- package/src/modules/shipping_carriers/i18n/en.json +7 -0
- package/src/modules/shipping_carriers/i18n/en.ts +7 -0
- package/src/modules/shipping_carriers/i18n/es.json +7 -0
- package/src/modules/shipping_carriers/i18n/pl.json +7 -0
- package/src/modules/shipping_carriers/i18n/pl.ts +7 -0
- package/src/modules/shipping_carriers/index.ts +5 -0
- package/src/modules/shipping_carriers/lib/adapter-registry.ts +33 -0
- package/src/modules/shipping_carriers/lib/adapter.ts +93 -0
- package/src/modules/shipping_carriers/lib/queue.ts +19 -0
- package/src/modules/shipping_carriers/lib/shipping-service.ts +204 -0
- package/src/modules/shipping_carriers/lib/status-sync.ts +38 -0
- package/src/modules/shipping_carriers/migrations/Migration20260305170000.ts +14 -0
- package/src/modules/shipping_carriers/setup.ts +11 -0
- package/src/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.ts +24 -0
- package/src/modules/shipping_carriers/widgets/injection/tracking-column/widget.ts +22 -0
- package/src/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.tsx +44 -0
- package/src/modules/shipping_carriers/widgets/injection-table.ts +22 -0
- package/src/modules/shipping_carriers/workers/status-poller.ts +33 -0
- package/src/modules/shipping_carriers/workers/webhook-processor.ts +79 -0
- package/src/modules/translations/api/get/locales.ts +1 -0
- package/src/modules/translations/api/put/locales.ts +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { InjectionFieldWidget } from '@open-mercato/shared/modules/widgets/injection'
|
|
2
|
+
|
|
3
|
+
const widget: InjectionFieldWidget = {
|
|
4
|
+
metadata: {
|
|
5
|
+
id: 'sales.injection.payment-gateway-config-field',
|
|
6
|
+
priority: 40,
|
|
7
|
+
},
|
|
8
|
+
fields: [
|
|
9
|
+
{
|
|
10
|
+
id: 'payment_gateways.captureMethod',
|
|
11
|
+
label: 'payment_gateways.field.captureMethod',
|
|
12
|
+
type: 'select',
|
|
13
|
+
group: 'details',
|
|
14
|
+
options: [
|
|
15
|
+
{ value: 'automatic', label: 'payment_gateways.captureMethod.automatic' },
|
|
16
|
+
{ value: 'manual', label: 'payment_gateways.captureMethod.manual' },
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'payment_gateways.paymentTypes',
|
|
21
|
+
label: 'payment_gateways.field.paymentTypes',
|
|
22
|
+
type: 'text',
|
|
23
|
+
group: 'details',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default widget
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { InjectionColumnWidget } from '@open-mercato/shared/modules/widgets/injection'
|
|
2
|
+
|
|
3
|
+
const widget: InjectionColumnWidget = {
|
|
4
|
+
metadata: {
|
|
5
|
+
id: 'sales.injection.payment-gateway-status-column',
|
|
6
|
+
priority: 50,
|
|
7
|
+
},
|
|
8
|
+
columns: [
|
|
9
|
+
{
|
|
10
|
+
id: 'gateway_status',
|
|
11
|
+
header: 'payment_gateways.column.gatewayStatus',
|
|
12
|
+
accessorKey: '_gateway.unifiedStatus',
|
|
13
|
+
sortable: false,
|
|
14
|
+
cell: ({ getValue }) => {
|
|
15
|
+
const value = getValue()
|
|
16
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : 'pending'
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default widget
|
|
@@ -17,6 +17,18 @@ export const injectionTable: ModuleInjectionTable = {
|
|
|
17
17
|
priority: 50,
|
|
18
18
|
},
|
|
19
19
|
],
|
|
20
|
+
'data-table:sales.payments:columns': {
|
|
21
|
+
widgetId: 'sales.injection.payment-gateway-status-column',
|
|
22
|
+
priority: 50,
|
|
23
|
+
},
|
|
24
|
+
'crud-form:sales.payment_method:fields': {
|
|
25
|
+
widgetId: 'sales.injection.payment-gateway-config-field',
|
|
26
|
+
priority: 40,
|
|
27
|
+
},
|
|
28
|
+
'crud-form:sales.sales_payment_method:fields': {
|
|
29
|
+
widgetId: 'sales.injection.payment-gateway-config-field',
|
|
30
|
+
priority: 40,
|
|
31
|
+
},
|
|
20
32
|
}
|
|
21
33
|
|
|
22
34
|
export default injectionTable
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const features = [
|
|
2
|
+
{ id: 'shipping_carriers.view', title: 'View shipping carrier shipments', module: 'shipping_carriers' },
|
|
3
|
+
{ id: 'shipping_carriers.manage', title: 'Manage shipping carrier operations', module: 'shipping_carriers' },
|
|
4
|
+
]
|
|
5
|
+
|
|
6
|
+
export default features
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
5
|
+
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
6
|
+
import { cancelShipmentSchema } from '../../data/validators'
|
|
7
|
+
import { shippingCarriersTag } from '../openapi'
|
|
8
|
+
|
|
9
|
+
export const metadata = {
|
|
10
|
+
path: '/shipping-carriers/cancel',
|
|
11
|
+
POST: { requireAuth: true, requireFeatures: ['shipping_carriers.manage'] },
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function POST(req: Request) {
|
|
15
|
+
const auth = await getAuthFromRequest(req)
|
|
16
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
17
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
18
|
+
}
|
|
19
|
+
const payload = await readJsonSafe<unknown>(req)
|
|
20
|
+
const parsed = cancelShipmentSchema.safeParse(payload)
|
|
21
|
+
if (!parsed.success) {
|
|
22
|
+
return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })
|
|
23
|
+
}
|
|
24
|
+
const container = await createRequestContainer()
|
|
25
|
+
const service = container.resolve('shippingCarrierService') as ShippingCarrierService
|
|
26
|
+
try {
|
|
27
|
+
const result = await service.cancelShipment({
|
|
28
|
+
...parsed.data,
|
|
29
|
+
organizationId: auth.orgId as string,
|
|
30
|
+
tenantId: auth.tenantId,
|
|
31
|
+
})
|
|
32
|
+
return NextResponse.json(result)
|
|
33
|
+
} catch (error: unknown) {
|
|
34
|
+
const message = error instanceof Error ? error.message : 'Failed to cancel shipment'
|
|
35
|
+
return NextResponse.json({ error: message }, { status: 502 })
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const openApi = {
|
|
40
|
+
tags: [shippingCarriersTag],
|
|
41
|
+
summary: 'Cancel shipment',
|
|
42
|
+
methods: {
|
|
43
|
+
POST: {
|
|
44
|
+
summary: 'Cancel shipment',
|
|
45
|
+
tags: [shippingCarriersTag],
|
|
46
|
+
responses: [
|
|
47
|
+
{ status: 200, description: 'Shipment cancelled' },
|
|
48
|
+
{ status: 422, description: 'Validation failed' },
|
|
49
|
+
{ status: 502, description: 'Provider upstream error' },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-interceptor'
|
|
2
|
+
import { getShippingAdapter } from '../lib/adapter-registry'
|
|
3
|
+
|
|
4
|
+
export const interceptors: ApiInterceptor[] = [
|
|
5
|
+
{
|
|
6
|
+
id: 'shipping_carriers.validate-provider',
|
|
7
|
+
targetRoute: 'shipping-carriers/*',
|
|
8
|
+
methods: ['POST', 'GET'],
|
|
9
|
+
priority: 100,
|
|
10
|
+
async before(request) {
|
|
11
|
+
const providerKey = (request.body?.providerKey ?? request.query?.providerKey) as string | undefined
|
|
12
|
+
if (!providerKey || providerKey.trim().length === 0) return { ok: true }
|
|
13
|
+
if (!getShippingAdapter(providerKey.trim())) {
|
|
14
|
+
return { ok: false, statusCode: 422, message: `Unknown shipping provider: ${providerKey}` }
|
|
15
|
+
}
|
|
16
|
+
return { ok: true }
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const shippingCarriersTag = 'ShippingCarriers'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
5
|
+
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
6
|
+
import { calculateRatesSchema } from '../../data/validators'
|
|
7
|
+
import { shippingCarriersTag } from '../openapi'
|
|
8
|
+
|
|
9
|
+
export const metadata = {
|
|
10
|
+
path: '/shipping-carriers/rates',
|
|
11
|
+
POST: { requireAuth: true, requireFeatures: ['shipping_carriers.manage'] },
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function POST(req: Request) {
|
|
15
|
+
const auth = await getAuthFromRequest(req)
|
|
16
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
17
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
18
|
+
}
|
|
19
|
+
const payload = await readJsonSafe<unknown>(req)
|
|
20
|
+
const parsed = calculateRatesSchema.safeParse(payload)
|
|
21
|
+
if (!parsed.success) {
|
|
22
|
+
return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })
|
|
23
|
+
}
|
|
24
|
+
const container = await createRequestContainer()
|
|
25
|
+
const service = container.resolve('shippingCarrierService') as ShippingCarrierService
|
|
26
|
+
try {
|
|
27
|
+
const rates = await service.calculateRates({
|
|
28
|
+
...parsed.data,
|
|
29
|
+
organizationId: auth.orgId as string,
|
|
30
|
+
tenantId: auth.tenantId,
|
|
31
|
+
})
|
|
32
|
+
return NextResponse.json({ rates })
|
|
33
|
+
} catch (error: unknown) {
|
|
34
|
+
const message = error instanceof Error ? error.message : 'Failed to calculate rates'
|
|
35
|
+
return NextResponse.json({ error: message }, { status: 502 })
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const openApi = {
|
|
40
|
+
tags: [shippingCarriersTag],
|
|
41
|
+
summary: 'Calculate shipping rates',
|
|
42
|
+
methods: {
|
|
43
|
+
POST: {
|
|
44
|
+
summary: 'Calculate shipping rates',
|
|
45
|
+
tags: [shippingCarriersTag],
|
|
46
|
+
responses: [
|
|
47
|
+
{ status: 200, description: 'Rates calculated' },
|
|
48
|
+
{ status: 422, description: 'Validation failed' },
|
|
49
|
+
{ status: 502, description: 'Provider upstream error' },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
5
|
+
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
6
|
+
import { createShipmentSchema } from '../../data/validators'
|
|
7
|
+
import { shippingCarriersTag } from '../openapi'
|
|
8
|
+
|
|
9
|
+
export const metadata = {
|
|
10
|
+
path: '/shipping-carriers/shipments',
|
|
11
|
+
POST: { requireAuth: true, requireFeatures: ['shipping_carriers.manage'] },
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function POST(req: Request) {
|
|
15
|
+
const auth = await getAuthFromRequest(req)
|
|
16
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
17
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
18
|
+
}
|
|
19
|
+
const payload = await readJsonSafe<unknown>(req)
|
|
20
|
+
const parsed = createShipmentSchema.safeParse(payload)
|
|
21
|
+
if (!parsed.success) {
|
|
22
|
+
return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })
|
|
23
|
+
}
|
|
24
|
+
const container = await createRequestContainer()
|
|
25
|
+
const service = container.resolve('shippingCarrierService') as ShippingCarrierService
|
|
26
|
+
try {
|
|
27
|
+
const shipment = await service.createShipment({
|
|
28
|
+
...parsed.data,
|
|
29
|
+
organizationId: auth.orgId as string,
|
|
30
|
+
tenantId: auth.tenantId,
|
|
31
|
+
})
|
|
32
|
+
return NextResponse.json({
|
|
33
|
+
shipmentId: shipment.id,
|
|
34
|
+
carrierShipmentId: shipment.carrierShipmentId,
|
|
35
|
+
trackingNumber: shipment.trackingNumber,
|
|
36
|
+
status: shipment.unifiedStatus,
|
|
37
|
+
labelUrl: shipment.labelUrl,
|
|
38
|
+
}, { status: 201 })
|
|
39
|
+
} catch (error: unknown) {
|
|
40
|
+
const message = error instanceof Error ? error.message : 'Failed to create shipment'
|
|
41
|
+
return NextResponse.json({ error: message }, { status: 502 })
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const openApi = {
|
|
46
|
+
tags: [shippingCarriersTag],
|
|
47
|
+
summary: 'Create shipment',
|
|
48
|
+
methods: {
|
|
49
|
+
POST: {
|
|
50
|
+
summary: 'Create shipment',
|
|
51
|
+
tags: [shippingCarriersTag],
|
|
52
|
+
responses: [
|
|
53
|
+
{ status: 201, description: 'Shipment created' },
|
|
54
|
+
{ status: 422, description: 'Validation failed' },
|
|
55
|
+
{ status: 502, description: 'Provider upstream error' },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
5
|
+
import { trackingQuerySchema } from '../../data/validators'
|
|
6
|
+
import { shippingCarriersTag } from '../openapi'
|
|
7
|
+
|
|
8
|
+
export const metadata = {
|
|
9
|
+
path: '/shipping-carriers/tracking',
|
|
10
|
+
GET: { requireAuth: true, requireFeatures: ['shipping_carriers.view'] },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function GET(req: Request) {
|
|
14
|
+
const auth = await getAuthFromRequest(req)
|
|
15
|
+
if (!auth?.tenantId || !auth.orgId) {
|
|
16
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
17
|
+
}
|
|
18
|
+
const url = new URL(req.url)
|
|
19
|
+
const parsed = trackingQuerySchema.safeParse({
|
|
20
|
+
providerKey: url.searchParams.get('providerKey'),
|
|
21
|
+
shipmentId: url.searchParams.get('shipmentId') ?? undefined,
|
|
22
|
+
trackingNumber: url.searchParams.get('trackingNumber') ?? undefined,
|
|
23
|
+
})
|
|
24
|
+
if (!parsed.success) {
|
|
25
|
+
return NextResponse.json({ error: 'Invalid query', details: parsed.error.flatten() }, { status: 422 })
|
|
26
|
+
}
|
|
27
|
+
const container = await createRequestContainer()
|
|
28
|
+
const service = container.resolve('shippingCarrierService') as ShippingCarrierService
|
|
29
|
+
try {
|
|
30
|
+
const tracking = await service.getTracking({
|
|
31
|
+
...parsed.data,
|
|
32
|
+
organizationId: auth.orgId as string,
|
|
33
|
+
tenantId: auth.tenantId,
|
|
34
|
+
})
|
|
35
|
+
return NextResponse.json(tracking)
|
|
36
|
+
} catch (error: unknown) {
|
|
37
|
+
const message = error instanceof Error ? error.message : 'Failed to fetch tracking'
|
|
38
|
+
return NextResponse.json({ error: message }, { status: 502 })
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const openApi = {
|
|
43
|
+
tags: [shippingCarriersTag],
|
|
44
|
+
summary: 'Get tracking',
|
|
45
|
+
methods: {
|
|
46
|
+
GET: {
|
|
47
|
+
summary: 'Get tracking',
|
|
48
|
+
tags: [shippingCarriersTag],
|
|
49
|
+
responses: [
|
|
50
|
+
{ status: 200, description: 'Tracking returned' },
|
|
51
|
+
{ status: 422, description: 'Validation failed' },
|
|
52
|
+
{ status: 502, description: 'Provider upstream error' },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
3
|
+
import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
4
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
5
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
6
|
+
import type { CredentialsService } from '../../../../integrations/lib/credentials-service'
|
|
7
|
+
import { CarrierShipment } from '../../../data/entities'
|
|
8
|
+
import { getShippingAdapter } from '../../../lib/adapter-registry'
|
|
9
|
+
import type { ShippingCarrierService } from '../../../lib/shipping-service'
|
|
10
|
+
import { getShippingCarrierQueue } from '../../../lib/queue'
|
|
11
|
+
import { shippingCarriersTag } from '../../openapi'
|
|
12
|
+
|
|
13
|
+
export const metadata = {
|
|
14
|
+
path: '/shipping-carriers/webhook/[provider]',
|
|
15
|
+
POST: { requireAuth: false },
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readCarrierShipmentId(payload: Record<string, unknown> | null): string | null {
|
|
19
|
+
if (!payload) return null
|
|
20
|
+
const shipmentId = payload.shipmentId
|
|
21
|
+
if (typeof shipmentId === 'string' && shipmentId.trim().length > 0) return shipmentId.trim()
|
|
22
|
+
const data = payload.data
|
|
23
|
+
if (data && typeof data === 'object') {
|
|
24
|
+
const nested = (data as Record<string, unknown>).shipmentId
|
|
25
|
+
if (typeof nested === 'string' && nested.trim().length > 0) return nested.trim()
|
|
26
|
+
}
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function POST(req: Request, { params }: { params: Promise<{ provider: string }> | { provider: string } }) {
|
|
31
|
+
const resolvedParams = await params
|
|
32
|
+
const providerKey = resolvedParams.provider
|
|
33
|
+
const adapter = getShippingAdapter(providerKey)
|
|
34
|
+
if (!adapter) {
|
|
35
|
+
return NextResponse.json({ error: `No shipping adapter for provider: ${providerKey}` }, { status: 404 })
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const rawBody = await req.text()
|
|
39
|
+
const headers: Record<string, string> = {}
|
|
40
|
+
req.headers.forEach((value, key) => {
|
|
41
|
+
headers[key] = value
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const container = await createRequestContainer()
|
|
45
|
+
const service = container.resolve('shippingCarrierService') as ShippingCarrierService
|
|
46
|
+
const em = container.resolve('em') as EntityManager
|
|
47
|
+
const integrationCredentialsService = container.resolve('integrationCredentialsService') as CredentialsService
|
|
48
|
+
const queue = getShippingCarrierQueue('shipping-carriers-webhook')
|
|
49
|
+
const payload = await readJsonSafe<Record<string, unknown>>(rawBody)
|
|
50
|
+
const carrierShipmentId = readCarrierShipmentId(payload)
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const candidates = carrierShipmentId
|
|
54
|
+
? await findWithDecryption(
|
|
55
|
+
em,
|
|
56
|
+
CarrierShipment,
|
|
57
|
+
{
|
|
58
|
+
providerKey,
|
|
59
|
+
carrierShipmentId,
|
|
60
|
+
deletedAt: null,
|
|
61
|
+
},
|
|
62
|
+
{ limit: 10, orderBy: { createdAt: 'desc' } },
|
|
63
|
+
)
|
|
64
|
+
: []
|
|
65
|
+
|
|
66
|
+
let shipment = null as CarrierShipment | null
|
|
67
|
+
let matchedScope = null as { organizationId: string; tenantId: string } | null
|
|
68
|
+
let event = null as Awaited<ReturnType<typeof adapter.verifyWebhook>> | null
|
|
69
|
+
let lastVerificationError: unknown = null
|
|
70
|
+
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
const candidateScope = { organizationId: candidate.organizationId, tenantId: candidate.tenantId }
|
|
73
|
+
const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, candidateScope) ?? {}
|
|
74
|
+
try {
|
|
75
|
+
event = await adapter.verifyWebhook({ rawBody, headers, credentials })
|
|
76
|
+
shipment = candidate
|
|
77
|
+
matchedScope = candidateScope
|
|
78
|
+
break
|
|
79
|
+
} catch (error: unknown) {
|
|
80
|
+
lastVerificationError = error
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!event) {
|
|
85
|
+
try {
|
|
86
|
+
event = await adapter.verifyWebhook({ rawBody, headers, credentials: {} })
|
|
87
|
+
} catch (error: unknown) {
|
|
88
|
+
throw lastVerificationError ?? error
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!event) {
|
|
92
|
+
throw new Error('Webhook verification failed')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!shipment && carrierShipmentId && matchedScope) {
|
|
96
|
+
shipment = await service.findShipmentByCarrierId(providerKey, carrierShipmentId, matchedScope)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const scope = shipment
|
|
100
|
+
? { organizationId: shipment.organizationId, tenantId: shipment.tenantId }
|
|
101
|
+
: matchedScope
|
|
102
|
+
|
|
103
|
+
await queue.enqueue({
|
|
104
|
+
name: 'shipping-carrier-webhook',
|
|
105
|
+
payload: {
|
|
106
|
+
providerKey,
|
|
107
|
+
event,
|
|
108
|
+
shipmentId: shipment?.id ?? null,
|
|
109
|
+
scope,
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return NextResponse.json({ received: true, queued: true }, { status: 202 })
|
|
114
|
+
} catch (error: unknown) {
|
|
115
|
+
const message = error instanceof Error ? error.message : 'Webhook verification failed'
|
|
116
|
+
return NextResponse.json({ error: message }, { status: 401 })
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const openApi = {
|
|
121
|
+
tags: [shippingCarriersTag],
|
|
122
|
+
summary: 'Receive shipping carrier webhook',
|
|
123
|
+
methods: {
|
|
124
|
+
POST: {
|
|
125
|
+
summary: 'Process inbound carrier webhook',
|
|
126
|
+
tags: [shippingCarriersTag],
|
|
127
|
+
responses: [
|
|
128
|
+
{ status: 202, description: 'Webhook accepted for async processing' },
|
|
129
|
+
{ status: 401, description: 'Signature verification failed' },
|
|
130
|
+
{ status: 404, description: 'Unknown provider' },
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ResponseEnricher } from '@open-mercato/shared/lib/crud/response-enricher'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
4
|
+
import { CarrierShipment } from './entities'
|
|
5
|
+
|
|
6
|
+
type SalesShipmentRecord = Record<string, unknown> & { id?: string; shipmentId?: string; orderId?: string }
|
|
7
|
+
|
|
8
|
+
const salesShipmentCarrierEnricher: ResponseEnricher<SalesShipmentRecord, Record<string, unknown>> = {
|
|
9
|
+
id: 'shipping_carriers.sales-shipment-carrier',
|
|
10
|
+
targetEntity: 'sales.shipment',
|
|
11
|
+
priority: 40,
|
|
12
|
+
timeout: 2000,
|
|
13
|
+
async enrichOne(record, context) {
|
|
14
|
+
const orderId = typeof record.orderId === 'string' ? record.orderId : null
|
|
15
|
+
if (!orderId) return record
|
|
16
|
+
const em = context.em as EntityManager
|
|
17
|
+
const scope = { organizationId: context.organizationId, tenantId: context.tenantId }
|
|
18
|
+
const shipment = await findOneWithDecryption(
|
|
19
|
+
em,
|
|
20
|
+
CarrierShipment,
|
|
21
|
+
{
|
|
22
|
+
orderId,
|
|
23
|
+
organizationId: context.organizationId,
|
|
24
|
+
tenantId: context.tenantId,
|
|
25
|
+
deletedAt: null,
|
|
26
|
+
},
|
|
27
|
+
{ orderBy: { createdAt: 'desc' } },
|
|
28
|
+
scope,
|
|
29
|
+
)
|
|
30
|
+
if (!shipment) return record
|
|
31
|
+
return {
|
|
32
|
+
...record,
|
|
33
|
+
_carrier: {
|
|
34
|
+
shipmentId: shipment.id,
|
|
35
|
+
providerKey: shipment.providerKey,
|
|
36
|
+
trackingNumber: shipment.trackingNumber,
|
|
37
|
+
status: shipment.unifiedStatus,
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
async enrichMany(records, context) {
|
|
42
|
+
const orderIds = Array.from(new Set(
|
|
43
|
+
records
|
|
44
|
+
.map((record) => (typeof record.orderId === 'string' ? record.orderId : null))
|
|
45
|
+
.filter((value): value is string => Boolean(value)),
|
|
46
|
+
))
|
|
47
|
+
if (!orderIds.length) return records
|
|
48
|
+
|
|
49
|
+
const em = context.em as EntityManager
|
|
50
|
+
const scope = { organizationId: context.organizationId, tenantId: context.tenantId }
|
|
51
|
+
const shipments = await findWithDecryption(
|
|
52
|
+
em,
|
|
53
|
+
CarrierShipment,
|
|
54
|
+
{
|
|
55
|
+
orderId: { $in: orderIds },
|
|
56
|
+
organizationId: context.organizationId,
|
|
57
|
+
tenantId: context.tenantId,
|
|
58
|
+
deletedAt: null,
|
|
59
|
+
},
|
|
60
|
+
{ orderBy: { createdAt: 'desc' } },
|
|
61
|
+
scope,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const latestByOrderId = new Map<string, CarrierShipment>()
|
|
65
|
+
for (const shipment of shipments) {
|
|
66
|
+
if (!latestByOrderId.has(shipment.orderId)) {
|
|
67
|
+
latestByOrderId.set(shipment.orderId, shipment)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return records.map((record) => {
|
|
72
|
+
const orderId = typeof record.orderId === 'string' ? record.orderId : null
|
|
73
|
+
if (!orderId) return record
|
|
74
|
+
const shipment = latestByOrderId.get(orderId)
|
|
75
|
+
if (!shipment) return record
|
|
76
|
+
return {
|
|
77
|
+
...record,
|
|
78
|
+
_carrier: {
|
|
79
|
+
shipmentId: shipment.id,
|
|
80
|
+
providerKey: shipment.providerKey,
|
|
81
|
+
trackingNumber: shipment.trackingNumber,
|
|
82
|
+
status: shipment.unifiedStatus,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const enrichers: ResponseEnricher[] = [salesShipmentCarrierEnricher]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core'
|
|
2
|
+
|
|
3
|
+
@Entity({ tableName: 'carrier_shipments' })
|
|
4
|
+
@Index({ properties: ['orderId', 'organizationId', 'tenantId'] })
|
|
5
|
+
@Index({ properties: ['providerKey', 'carrierShipmentId', 'organizationId'] })
|
|
6
|
+
@Index({ properties: ['organizationId', 'tenantId', 'unifiedStatus'] })
|
|
7
|
+
export class CarrierShipment {
|
|
8
|
+
[OptionalProps]?: 'labelUrl' | 'labelData' | 'trackingEvents' | 'carrierStatus' | 'lastWebhookAt' | 'lastPolledAt' | 'createdAt' | 'updatedAt' | 'deletedAt'
|
|
9
|
+
|
|
10
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
11
|
+
id!: string
|
|
12
|
+
|
|
13
|
+
@Property({ name: 'order_id', type: 'uuid' })
|
|
14
|
+
orderId!: string
|
|
15
|
+
|
|
16
|
+
@Property({ name: 'provider_key', type: 'text' })
|
|
17
|
+
providerKey!: string
|
|
18
|
+
|
|
19
|
+
@Property({ name: 'carrier_shipment_id', type: 'text' })
|
|
20
|
+
carrierShipmentId!: string
|
|
21
|
+
|
|
22
|
+
@Property({ name: 'tracking_number', type: 'text' })
|
|
23
|
+
trackingNumber!: string
|
|
24
|
+
|
|
25
|
+
@Property({ name: 'unified_status', type: 'text' })
|
|
26
|
+
unifiedStatus: string = 'label_created'
|
|
27
|
+
|
|
28
|
+
@Property({ name: 'carrier_status', type: 'text', nullable: true })
|
|
29
|
+
carrierStatus?: string | null
|
|
30
|
+
|
|
31
|
+
@Property({ name: 'label_url', type: 'text', nullable: true })
|
|
32
|
+
labelUrl?: string | null
|
|
33
|
+
|
|
34
|
+
@Property({ name: 'label_data', type: 'text', nullable: true })
|
|
35
|
+
labelData?: string | null
|
|
36
|
+
|
|
37
|
+
@Property({ name: 'tracking_events', type: 'jsonb', nullable: true })
|
|
38
|
+
trackingEvents?: Array<{ status: string; occurredAt: string; location?: string }> | null
|
|
39
|
+
|
|
40
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
41
|
+
organizationId!: string
|
|
42
|
+
|
|
43
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
44
|
+
tenantId!: string
|
|
45
|
+
|
|
46
|
+
@Property({ name: 'last_webhook_at', type: Date, nullable: true })
|
|
47
|
+
lastWebhookAt?: Date | null
|
|
48
|
+
|
|
49
|
+
@Property({ name: 'last_polled_at', type: Date, nullable: true })
|
|
50
|
+
lastPolledAt?: Date | null
|
|
51
|
+
|
|
52
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
53
|
+
createdAt: Date = new Date()
|
|
54
|
+
|
|
55
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
56
|
+
updatedAt: Date = new Date()
|
|
57
|
+
|
|
58
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
59
|
+
deletedAt?: Date | null
|
|
60
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
const addressSchema = z.object({
|
|
4
|
+
countryCode: z.string().min(2).max(3),
|
|
5
|
+
postalCode: z.string().min(1),
|
|
6
|
+
city: z.string().min(1),
|
|
7
|
+
line1: z.string().min(1),
|
|
8
|
+
line2: z.string().optional(),
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const packageSchema = z.object({
|
|
12
|
+
weightKg: z.number().positive(),
|
|
13
|
+
lengthCm: z.number().positive(),
|
|
14
|
+
widthCm: z.number().positive(),
|
|
15
|
+
heightCm: z.number().positive(),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
export const calculateRatesSchema = z.object({
|
|
19
|
+
providerKey: z.string().min(1),
|
|
20
|
+
origin: addressSchema,
|
|
21
|
+
destination: addressSchema,
|
|
22
|
+
packages: z.array(packageSchema).min(1),
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export const createShipmentSchema = z.object({
|
|
26
|
+
providerKey: z.string().min(1),
|
|
27
|
+
orderId: z.string().uuid(),
|
|
28
|
+
origin: addressSchema,
|
|
29
|
+
destination: addressSchema,
|
|
30
|
+
packages: z.array(packageSchema).min(1),
|
|
31
|
+
serviceCode: z.string().min(1),
|
|
32
|
+
labelFormat: z.enum(['pdf', 'zpl', 'png']).optional(),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export const trackingQuerySchema = z.object({
|
|
36
|
+
providerKey: z.string().min(1),
|
|
37
|
+
shipmentId: z.string().uuid().optional(),
|
|
38
|
+
trackingNumber: z.string().min(1).optional(),
|
|
39
|
+
}).refine((value) => Boolean(value.shipmentId || value.trackingNumber), {
|
|
40
|
+
message: 'shipmentId or trackingNumber is required',
|
|
41
|
+
path: ['shipmentId'],
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
export const cancelShipmentSchema = z.object({
|
|
45
|
+
providerKey: z.string().min(1),
|
|
46
|
+
shipmentId: z.string().uuid(),
|
|
47
|
+
reason: z.string().max(200).optional(),
|
|
48
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { asFunction, asValue } from 'awilix'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import type { AppContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import type { CredentialsService } from '../integrations/lib/credentials-service'
|
|
5
|
+
import { CarrierShipment } from './data/entities'
|
|
6
|
+
import { createShippingCarrierService } from './lib/shipping-service'
|
|
7
|
+
|
|
8
|
+
type Cradle = {
|
|
9
|
+
em: EntityManager
|
|
10
|
+
integrationCredentialsService: CredentialsService
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function register(container: AppContainer) {
|
|
14
|
+
container.register({
|
|
15
|
+
shippingCarrierService: asFunction(({ em, integrationCredentialsService }: Cradle) =>
|
|
16
|
+
createShippingCarrierService({ em, integrationCredentialsService }),
|
|
17
|
+
).scoped().proxy(),
|
|
18
|
+
CarrierShipment: asValue(CarrierShipment),
|
|
19
|
+
})
|
|
20
|
+
}
|