@open-mercato/core 0.4.9-develop-ce96cffe00 → 0.4.9-develop-5d884303ad

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.
Files changed (63) hide show
  1. package/dist/modules/auth/lib/setup-app.js +17 -1
  2. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  3. package/dist/modules/sales/backend/sales/documents/[id]/page.js +23 -12
  4. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  5. package/dist/modules/shipping_carriers/api/cancel/route.js +5 -1
  6. package/dist/modules/shipping_carriers/api/cancel/route.js.map +2 -2
  7. package/dist/modules/shipping_carriers/api/points/route.js +59 -0
  8. package/dist/modules/shipping_carriers/api/points/route.js.map +7 -0
  9. package/dist/modules/shipping_carriers/api/providers/route.js +38 -0
  10. package/dist/modules/shipping_carriers/api/providers/route.js.map +7 -0
  11. package/dist/modules/shipping_carriers/backend/shipping-carriers/create/page.js +90 -0
  12. package/dist/modules/shipping_carriers/backend/shipping-carriers/create/page.js.map +7 -0
  13. package/dist/modules/shipping_carriers/backend/shipping-carriers/create/page.meta.js +8 -0
  14. package/dist/modules/shipping_carriers/backend/shipping-carriers/create/page.meta.js.map +7 -0
  15. package/dist/modules/shipping_carriers/data/validators.js +17 -2
  16. package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
  17. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/AddressFields.js +76 -0
  18. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/AddressFields.js.map +7 -0
  19. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js +243 -0
  20. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js.map +7 -0
  21. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfirmStep.js +134 -0
  22. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfirmStep.js.map +7 -0
  23. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.js +70 -0
  24. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.js.map +7 -0
  25. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ProviderStep.js +32 -0
  26. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ProviderStep.js.map +7 -0
  27. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/WizardNav.js +37 -0
  28. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/WizardNav.js.map +7 -0
  29. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.js +92 -0
  30. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.js.map +7 -0
  31. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +212 -0
  32. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +7 -0
  33. package/dist/modules/shipping_carriers/lib/shipment-wizard/types.js +1 -0
  34. package/dist/modules/shipping_carriers/lib/shipment-wizard/types.js.map +7 -0
  35. package/dist/modules/shipping_carriers/lib/shipping-service.js +36 -3
  36. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
  37. package/dist/modules/shipping_carriers/lib/status-sync.js +7 -0
  38. package/dist/modules/shipping_carriers/lib/status-sync.js.map +2 -2
  39. package/package.json +7 -4
  40. package/src/modules/auth/lib/setup-app.ts +22 -0
  41. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +17 -9
  42. package/src/modules/shipping_carriers/api/cancel/route.ts +5 -1
  43. package/src/modules/shipping_carriers/api/points/route.ts +57 -0
  44. package/src/modules/shipping_carriers/api/providers/route.ts +35 -0
  45. package/src/modules/shipping_carriers/backend/shipping-carriers/create/page.meta.ts +4 -0
  46. package/src/modules/shipping_carriers/backend/shipping-carriers/create/page.tsx +93 -0
  47. package/src/modules/shipping_carriers/data/validators.ts +15 -0
  48. package/src/modules/shipping_carriers/i18n/de.json +66 -0
  49. package/src/modules/shipping_carriers/i18n/en.json +66 -0
  50. package/src/modules/shipping_carriers/i18n/es.json +66 -0
  51. package/src/modules/shipping_carriers/i18n/pl.json +66 -0
  52. package/src/modules/shipping_carriers/lib/adapter.ts +20 -0
  53. package/src/modules/shipping_carriers/lib/shipment-wizard/components/AddressFields.tsx +72 -0
  54. package/src/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.tsx +343 -0
  55. package/src/modules/shipping_carriers/lib/shipment-wizard/components/ConfirmStep.tsx +213 -0
  56. package/src/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.tsx +82 -0
  57. package/src/modules/shipping_carriers/lib/shipment-wizard/components/ProviderStep.tsx +54 -0
  58. package/src/modules/shipping_carriers/lib/shipment-wizard/components/WizardNav.tsx +46 -0
  59. package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.ts +153 -0
  60. package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +312 -0
  61. package/src/modules/shipping_carriers/lib/shipment-wizard/types.ts +76 -0
  62. package/src/modules/shipping_carriers/lib/shipping-service.ts +53 -3
  63. package/src/modules/shipping_carriers/lib/status-sync.ts +7 -0
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { match } from "ts-pattern";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ const STEP_ORDER = ["provider", "configure", "confirm"];
7
+ const WizardNav = (props) => {
8
+ const { step, onNavigate } = props;
9
+ const t = useT();
10
+ const stepIndex = STEP_ORDER.indexOf(step);
11
+ const stepLabels = {
12
+ provider: t("shipping_carriers.create.step.provider", "Select carrier"),
13
+ configure: t("shipping_carriers.create.step.configure", "Configure shipment"),
14
+ confirm: t("shipping_carriers.create.step.confirm", "Select service & confirm")
15
+ };
16
+ return /* @__PURE__ */ jsx("nav", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: STEP_ORDER.map((stepId, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
17
+ index > 0 ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u203A" }) : null,
18
+ /* @__PURE__ */ jsxs(
19
+ "span",
20
+ {
21
+ className: match({ isCurrent: stepId === step, isPast: index < stepIndex }).with({ isCurrent: true }, () => "font-medium text-foreground").with({ isPast: true }, () => "cursor-pointer hover:text-foreground").otherwise(() => ""),
22
+ onClick: () => {
23
+ if (index < stepIndex) onNavigate(stepId);
24
+ },
25
+ children: [
26
+ index + 1,
27
+ ". ",
28
+ stepLabels[stepId]
29
+ ]
30
+ }
31
+ )
32
+ ] }, stepId)) });
33
+ };
34
+ export {
35
+ WizardNav
36
+ };
37
+ //# sourceMappingURL=WizardNav.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/shipping_carriers/lib/shipment-wizard/components/WizardNav.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { match } from 'ts-pattern'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { WizardStep } from '../types'\n\nconst STEP_ORDER: WizardStep[] = ['provider', 'configure', 'confirm']\n\nexport type WizardNavProps = {\n step: WizardStep\n onNavigate: (step: WizardStep) => void\n}\n\nexport const WizardNav = (props: WizardNavProps) => {\n const { step, onNavigate } = props\n const t = useT()\n const stepIndex = STEP_ORDER.indexOf(step)\n\n const stepLabels: Record<WizardStep, string> = {\n provider: t('shipping_carriers.create.step.provider', 'Select carrier'),\n configure: t('shipping_carriers.create.step.configure', 'Configure shipment'),\n confirm: t('shipping_carriers.create.step.confirm', 'Select service & confirm'),\n }\n\n return (\n <nav className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {STEP_ORDER.map((stepId, index) => (\n <React.Fragment key={stepId}>\n {index > 0 ? <span aria-hidden>\u203A</span> : null}\n <span\n className={match({ isCurrent: stepId === step, isPast: index < stepIndex })\n .with({ isCurrent: true }, () => 'font-medium text-foreground')\n .with({ isPast: true }, () => 'cursor-pointer hover:text-foreground')\n .otherwise(() => '')}\n onClick={() => {\n if (index < stepIndex) onNavigate(stepId)\n }}\n >\n {index + 1}. {stepLabels[stepId]}\n </span>\n </React.Fragment>\n ))}\n </nav>\n )\n}\n"],
5
+ "mappings": ";AA6BuB,cACb,YADa;AA3BvB,YAAY,WAAW;AACvB,SAAS,aAAa;AACtB,SAAS,YAAY;AAGrB,MAAM,aAA2B,CAAC,YAAY,aAAa,SAAS;AAO7D,MAAM,YAAY,CAAC,UAA0B;AAClD,QAAM,EAAE,MAAM,WAAW,IAAI;AAC7B,QAAM,IAAI,KAAK;AACf,QAAM,YAAY,WAAW,QAAQ,IAAI;AAEzC,QAAM,aAAyC;AAAA,IAC7C,UAAU,EAAE,0CAA0C,gBAAgB;AAAA,IACtE,WAAW,EAAE,2CAA2C,oBAAoB;AAAA,IAC5E,SAAS,EAAE,yCAAyC,0BAA0B;AAAA,EAChF;AAEA,SACE,oBAAC,SAAI,WAAU,yDACZ,qBAAW,IAAI,CAAC,QAAQ,UACvB,qBAAC,MAAM,UAAN,EACE;AAAA,YAAQ,IAAI,oBAAC,UAAK,eAAW,MAAC,oBAAC,IAAU;AAAA,IAC1C;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,MAAM,EAAE,WAAW,WAAW,MAAM,QAAQ,QAAQ,UAAU,CAAC,EACvE,KAAK,EAAE,WAAW,KAAK,GAAG,MAAM,6BAA6B,EAC7D,KAAK,EAAE,QAAQ,KAAK,GAAG,MAAM,sCAAsC,EACnE,UAAU,MAAM,EAAE;AAAA,QACrB,SAAS,MAAM;AACb,cAAI,QAAQ,UAAW,YAAW,MAAM;AAAA,QAC1C;AAAA,QAEC;AAAA,kBAAQ;AAAA,UAAE;AAAA,UAAG,WAAW,MAAM;AAAA;AAAA;AAAA,IACjC;AAAA,OAZmB,MAarB,CACD,GACH;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,92 @@
1
+ import { match, P } from "ts-pattern";
2
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
3
+ const fetchProviders = async () => {
4
+ const call = await apiCall(
5
+ "/api/shipping-carriers/providers",
6
+ void 0,
7
+ { fallback: { providers: [] } }
8
+ );
9
+ return match(call).with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({
10
+ ok: true,
11
+ providers: result.providers
12
+ })).otherwise(() => ({ ok: false, error: "Failed to load shipping providers." }));
13
+ };
14
+ const fetchOrderAddresses = async (orderId) => {
15
+ const call = await apiCall(
16
+ `/api/sales/document-addresses?documentId=${orderId}&documentKind=order&pageSize=50`,
17
+ void 0,
18
+ { fallback: { items: [] } }
19
+ );
20
+ return match(call).with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({
21
+ ok: true,
22
+ items: result.items
23
+ })).otherwise(() => ({ ok: false }));
24
+ };
25
+ const fetchRates = async (params) => {
26
+ const { providerKey, origin, destination, packages, receiverPhone, receiverEmail } = params;
27
+ const call = await apiCall(
28
+ "/api/shipping-carriers/rates",
29
+ {
30
+ method: "POST",
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify({
33
+ providerKey,
34
+ origin,
35
+ destination,
36
+ packages,
37
+ ...receiverPhone !== void 0 ? { receiverPhone } : {},
38
+ ...receiverEmail !== void 0 ? { receiverEmail } : {}
39
+ })
40
+ },
41
+ { fallback: { rates: [] } }
42
+ );
43
+ return match(call).with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({
44
+ ok: true,
45
+ rates: result.rates
46
+ })).otherwise(({ result }) => ({
47
+ ok: false,
48
+ error: result?.error ?? "Failed to fetch shipping rates."
49
+ }));
50
+ };
51
+ const createShipment = async (params) => {
52
+ const call = await apiCall(
53
+ "/api/shipping-carriers/shipments",
54
+ {
55
+ method: "POST",
56
+ headers: { "Content-Type": "application/json" },
57
+ body: JSON.stringify(params)
58
+ },
59
+ { fallback: null }
60
+ );
61
+ return match(call).with({ ok: true }, () => ({ ok: true })).otherwise(({ result }) => ({
62
+ ok: false,
63
+ error: result?.error ?? "Failed to create shipment."
64
+ }));
65
+ };
66
+ const fetchDropOffPoints = async (params) => {
67
+ const url = new URL("/api/shipping-carriers/points", "http://placeholder");
68
+ url.searchParams.set("providerKey", params.providerKey);
69
+ if (params.query) url.searchParams.set("query", params.query);
70
+ if (params.type) url.searchParams.set("type", params.type);
71
+ if (params.postCode) url.searchParams.set("postCode", params.postCode);
72
+ const call = await apiCall(
73
+ `${url.pathname}${url.search}`,
74
+ void 0,
75
+ { fallback: { points: [] } }
76
+ );
77
+ return match(call).with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({
78
+ ok: true,
79
+ points: result.points
80
+ })).otherwise(({ result }) => ({
81
+ ok: false,
82
+ error: result?.error ?? "Failed to fetch drop-off points."
83
+ }));
84
+ };
85
+ export {
86
+ createShipment,
87
+ fetchDropOffPoints,
88
+ fetchOrderAddresses,
89
+ fetchProviders,
90
+ fetchRates
91
+ };
92
+ //# sourceMappingURL=shipmentApi.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.ts"],
4
+ "sourcesContent": ["import { match, P } from 'ts-pattern'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { Provider, Address, PackageDimension, ShippingRate, DocumentAddress, DropOffPoint } from '../types'\n\nexport type FetchProvidersResult =\n | { ok: true; providers: Provider[] }\n | { ok: false; error: string }\n\nexport type FetchOrderAddressesResult =\n | { ok: true; items: DocumentAddress[] }\n | { ok: false }\n\nexport type FetchRatesParams = {\n providerKey: string\n origin: Address\n destination: Address\n packages: PackageDimension[]\n receiverPhone?: string\n receiverEmail?: string\n}\n\nexport type FetchRatesResult =\n | { ok: true; rates: ShippingRate[] }\n | { ok: false; error: string }\n\nexport type CreateShipmentParams = {\n providerKey: string\n orderId: string\n origin: Address\n destination: Address\n packages: PackageDimension[]\n serviceCode: string\n labelFormat: string\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n}\n\nexport type CreateShipmentResult =\n | { ok: true }\n | { ok: false; error: string }\n\nexport type FetchDropOffPointsParams = {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n}\n\nexport type FetchDropOffPointsResult =\n | { ok: true; points: DropOffPoint[] }\n | { ok: false; error: string }\n\nexport const fetchProviders = async (): Promise<FetchProvidersResult> => {\n const call = await apiCall<{ providers: Provider[] }>(\n '/api/shipping-carriers/providers',\n undefined,\n { fallback: { providers: [] } },\n )\n return match(call)\n .with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({\n ok: true as const,\n providers: result.providers,\n }))\n .otherwise(() => ({ ok: false as const, error: 'Failed to load shipping providers.' }))\n}\n\nexport const fetchOrderAddresses = async (orderId: string): Promise<FetchOrderAddressesResult> => {\n const call = await apiCall<{ items: DocumentAddress[] }>(\n `/api/sales/document-addresses?documentId=${orderId}&documentKind=order&pageSize=50`,\n undefined,\n { fallback: { items: [] } },\n )\n return match(call)\n .with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({\n ok: true as const,\n items: result.items,\n }))\n .otherwise(() => ({ ok: false as const }))\n}\n\nexport const fetchRates = async (params: FetchRatesParams): Promise<FetchRatesResult> => {\n const { providerKey, origin, destination, packages, receiverPhone, receiverEmail } = params\n const call = await apiCall<{ rates: ShippingRate[] }>(\n '/api/shipping-carriers/rates',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n providerKey,\n origin,\n destination,\n packages,\n ...(receiverPhone !== undefined ? { receiverPhone } : {}),\n ...(receiverEmail !== undefined ? { receiverEmail } : {}),\n }),\n },\n { fallback: { rates: [] } },\n )\n return match(call)\n .with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({\n ok: true as const,\n rates: result.rates,\n }))\n .otherwise(({ result }) => ({\n ok: false as const,\n error: (result as { error?: string } | null)?.error ?? 'Failed to fetch shipping rates.',\n }))\n}\n\nexport const createShipment = async (params: CreateShipmentParams): Promise<CreateShipmentResult> => {\n const call = await apiCall(\n '/api/shipping-carriers/shipments',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(params),\n },\n { fallback: null },\n )\n return match(call)\n .with({ ok: true }, () => ({ ok: true as const }))\n .otherwise(({ result }) => ({\n ok: false as const,\n error: (result as { error?: string } | null)?.error ?? 'Failed to create shipment.',\n }))\n}\n\nexport const fetchDropOffPoints = async (params: FetchDropOffPointsParams): Promise<FetchDropOffPointsResult> => {\n const url = new URL('/api/shipping-carriers/points', 'http://placeholder')\n url.searchParams.set('providerKey', params.providerKey)\n if (params.query) url.searchParams.set('query', params.query)\n if (params.type) url.searchParams.set('type', params.type)\n if (params.postCode) url.searchParams.set('postCode', params.postCode)\n\n const call = await apiCall<{ points: DropOffPoint[] }>(\n `${url.pathname}${url.search}`,\n undefined,\n { fallback: { points: [] } },\n )\n return match(call)\n .with({ ok: true, result: P.not(P.nullish) }, ({ result }) => ({\n ok: true as const,\n points: result.points,\n }))\n .otherwise(({ result }) => ({\n ok: false as const,\n error: (result as { error?: string } | null)?.error ?? 'Failed to fetch drop-off points.',\n }))\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,212 @@
1
+ import * as React from "react";
2
+ import { useRouter, useSearchParams } from "next/navigation";
3
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import {
6
+ fetchProviders,
7
+ fetchOrderAddresses,
8
+ fetchRates,
9
+ createShipment,
10
+ fetchDropOffPoints
11
+ } from "./shipmentApi.js";
12
+ const EMPTY_ADDRESS = { countryCode: "", postalCode: "", city: "", line1: "" };
13
+ const EMPTY_CONTACT = { phone: "", email: "" };
14
+ const DEFAULT_PACKAGE = { weightKg: 1, lengthCm: 20, widthCm: 15, heightCm: 10 };
15
+ const buildAddressFromDocument = (doc) => ({
16
+ countryCode: doc.country ?? "",
17
+ postalCode: doc.postal_code ?? "",
18
+ city: doc.city ?? "",
19
+ line1: doc.address_line1,
20
+ line2: doc.address_line2 ?? void 0
21
+ });
22
+ const isAddressValid = (addr) => addr.countryCode.length >= 2 && addr.postalCode.length > 0 && addr.city.length > 0 && addr.line1.length > 0;
23
+ const isPackageValid = (pkg) => pkg.weightKg > 0 && pkg.lengthCm > 0 && pkg.widthCm > 0 && pkg.heightCm > 0;
24
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
25
+ const PHONE_REGEX = /^[+\d][\d\s\-().]{6,}$/;
26
+ const isEmailValid = (email) => email === "" || EMAIL_REGEX.test(email);
27
+ const isPhoneValid = (phone) => phone === "" || PHONE_REGEX.test(phone);
28
+ const useShipmentWizard = () => {
29
+ const t = useT();
30
+ const router = useRouter();
31
+ const searchParams = useSearchParams();
32
+ const orderId = searchParams?.get("orderId") ?? null;
33
+ const [step, setStep] = React.useState("provider");
34
+ const [providers, setProviders] = React.useState([]);
35
+ const [isLoadingProviders, setIsLoadingProviders] = React.useState(true);
36
+ const [providerError, setProviderError] = React.useState(null);
37
+ const [selectedProvider, setSelectedProvider] = React.useState(null);
38
+ const [origin, setOrigin] = React.useState(EMPTY_ADDRESS);
39
+ const [destination, setDestination] = React.useState(EMPTY_ADDRESS);
40
+ const [packages, setPackages] = React.useState([DEFAULT_PACKAGE]);
41
+ const [labelFormat, setLabelFormat] = React.useState("pdf");
42
+ const [senderContact, setSenderContact] = React.useState(EMPTY_CONTACT);
43
+ const [receiverContact, setReceiverContact] = React.useState(EMPTY_CONTACT);
44
+ const [targetPoint, setTargetPoint] = React.useState("");
45
+ const [c2cSendingMethod, setC2cSendingMethod] = React.useState("");
46
+ const [dropOffPointQuery, setDropOffPointQuery] = React.useState("");
47
+ const [dropOffPoints, setDropOffPoints] = React.useState([]);
48
+ const [isFetchingDropOffPoints, setIsFetchingDropOffPoints] = React.useState(false);
49
+ const [dropOffPointsError, setDropOffPointsError] = React.useState(null);
50
+ const [rates, setRates] = React.useState([]);
51
+ const [isFetchingRates, setIsFetchingRates] = React.useState(false);
52
+ const [ratesError, setRatesError] = React.useState(null);
53
+ const [selectedRate, setSelectedRate] = React.useState(null);
54
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
55
+ const backHref = orderId ? `/backend/sales/orders/${orderId}` : "/backend/sales/orders";
56
+ React.useEffect(() => {
57
+ const loadProviders = async () => {
58
+ setIsLoadingProviders(true);
59
+ setProviderError(null);
60
+ const result = await fetchProviders();
61
+ if (result.ok) {
62
+ setProviders(result.providers);
63
+ } else {
64
+ setProviderError(t("shipping_carriers.create.error.loadProviders", result.error));
65
+ }
66
+ setIsLoadingProviders(false);
67
+ };
68
+ void loadProviders();
69
+ }, [t]);
70
+ React.useEffect(() => {
71
+ if (!orderId) return;
72
+ const prefillAddresses = async () => {
73
+ const result = await fetchOrderAddresses(orderId);
74
+ if (!result.ok) return;
75
+ const items = result.items;
76
+ const shippingAddr = items.find((item) => item.purpose === "shipping") ?? items.find((item) => item.purpose === "delivery") ?? items[0];
77
+ if (shippingAddr) setDestination(buildAddressFromDocument(shippingAddr));
78
+ };
79
+ void prefillAddresses();
80
+ }, [orderId]);
81
+ const loadRates = async () => {
82
+ if (!selectedProvider) return;
83
+ setIsFetchingRates(true);
84
+ setRatesError(null);
85
+ setRates([]);
86
+ setSelectedRate(null);
87
+ const result = await fetchRates({
88
+ providerKey: selectedProvider,
89
+ origin,
90
+ destination,
91
+ packages,
92
+ ...receiverContact.phone ? { receiverPhone: receiverContact.phone } : {},
93
+ ...receiverContact.email ? { receiverEmail: receiverContact.email } : {}
94
+ });
95
+ if (result.ok) {
96
+ setRates(result.rates);
97
+ if (result.rates.length > 0) setSelectedRate(result.rates[0]);
98
+ } else {
99
+ setRatesError(t("shipping_carriers.create.error.fetchRates", result.error));
100
+ }
101
+ setIsFetchingRates(false);
102
+ };
103
+ const handleSubmit = async () => {
104
+ if (!selectedProvider || !selectedRate || !orderId) return;
105
+ setIsSubmitting(true);
106
+ const result = await createShipment({
107
+ providerKey: selectedProvider,
108
+ orderId,
109
+ origin,
110
+ destination,
111
+ packages,
112
+ serviceCode: selectedRate.serviceCode,
113
+ labelFormat,
114
+ ...senderContact.phone ? { senderPhone: senderContact.phone } : {},
115
+ ...senderContact.email ? { senderEmail: senderContact.email } : {},
116
+ ...receiverContact.phone ? { receiverPhone: receiverContact.phone } : {},
117
+ ...receiverContact.email ? { receiverEmail: receiverContact.email } : {},
118
+ ...targetPoint ? { targetPoint } : {},
119
+ ...c2cSendingMethod ? { c2cSendingMethod } : {}
120
+ });
121
+ setIsSubmitting(false);
122
+ if (result.ok) {
123
+ flash(t("shipping_carriers.create.success", "Shipment created successfully."), "success");
124
+ router.push(backHref);
125
+ } else {
126
+ flash(t("shipping_carriers.create.error.create", result.error), "error");
127
+ }
128
+ };
129
+ const goToStep = (next) => setStep(next);
130
+ const handleProviderSelect = (providerKey) => {
131
+ setSelectedProvider(providerKey);
132
+ goToStep("configure");
133
+ };
134
+ const handleConfigureNext = () => {
135
+ void loadRates().then(() => goToStep("confirm"));
136
+ };
137
+ const searchDropOffPoints = async (query) => {
138
+ if (!selectedProvider) return;
139
+ setDropOffPointQuery(query);
140
+ setIsFetchingDropOffPoints(true);
141
+ setDropOffPointsError(null);
142
+ const isPostCode = /^\d{2}-?\d{3}$/.test(query.trim());
143
+ const resolvedType = c2cSendingMethod === "pop" ? "pop" : "parcel_locker";
144
+ const result = await fetchDropOffPoints({
145
+ providerKey: selectedProvider,
146
+ ...isPostCode ? { postCode: query.trim() } : { query: query.trim() },
147
+ type: resolvedType
148
+ });
149
+ if (result.ok) {
150
+ setDropOffPoints(result.points);
151
+ } else {
152
+ setDropOffPointsError(t("shipping_carriers.create.error.fetchDropOffPoints", result.error));
153
+ setDropOffPoints([]);
154
+ }
155
+ setIsFetchingDropOffPoints(false);
156
+ };
157
+ const canProceedFromConfigure = isAddressValid(origin) && isAddressValid(destination) && packages.length > 0 && packages.every(isPackageValid) && isEmailValid(senderContact.email) && isPhoneValid(senderContact.phone) && isEmailValid(receiverContact.email) && isPhoneValid(receiverContact.phone);
158
+ const senderContactErrors = {
159
+ email: !isEmailValid(senderContact.email) ? t("shipping_carriers.create.error.invalidEmail", "Invalid email address.") : null,
160
+ phone: !isPhoneValid(senderContact.phone) ? t("shipping_carriers.create.error.invalidPhone", "Invalid phone number.") : null
161
+ };
162
+ const receiverContactErrors = {
163
+ email: !isEmailValid(receiverContact.email) ? t("shipping_carriers.create.error.invalidEmail", "Invalid email address.") : null,
164
+ phone: !isPhoneValid(receiverContact.phone) ? t("shipping_carriers.create.error.invalidPhone", "Invalid phone number.") : null
165
+ };
166
+ return {
167
+ step,
168
+ backHref,
169
+ providers,
170
+ isLoadingProviders,
171
+ providerError,
172
+ selectedProvider,
173
+ origin,
174
+ destination,
175
+ packages,
176
+ labelFormat,
177
+ senderContact,
178
+ receiverContact,
179
+ targetPoint,
180
+ c2cSendingMethod,
181
+ isFetchingRates,
182
+ canProceedFromConfigure,
183
+ senderContactErrors,
184
+ receiverContactErrors,
185
+ dropOffPointQuery,
186
+ dropOffPoints,
187
+ isFetchingDropOffPoints,
188
+ dropOffPointsError,
189
+ rates,
190
+ ratesError,
191
+ selectedRate,
192
+ isSubmitting,
193
+ goToStep,
194
+ handleProviderSelect,
195
+ handleConfigureNext,
196
+ handleSubmit: () => void handleSubmit(),
197
+ setOrigin,
198
+ setDestination,
199
+ setPackages,
200
+ setLabelFormat,
201
+ setSenderContact,
202
+ setReceiverContact,
203
+ setTargetPoint,
204
+ setC2cSendingMethod,
205
+ setSelectedRate,
206
+ searchDropOffPoints: (query) => void searchDropOffPoints(query)
207
+ };
208
+ };
209
+ export {
210
+ useShipmentWizard
211
+ };
212
+ //# sourceMappingURL=useShipmentWizard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts"],
4
+ "sourcesContent": ["import * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n fetchProviders,\n fetchOrderAddresses,\n fetchRates,\n createShipment,\n fetchDropOffPoints,\n} from './shipmentApi'\nimport type {\n WizardStep,\n Provider,\n Address,\n PackageDimension,\n ShippingRate,\n LabelFormat,\n DocumentAddress,\n ContactInfo,\n DropOffPoint,\n} from '../types'\n\nconst EMPTY_ADDRESS: Address = { countryCode: '', postalCode: '', city: '', line1: '' }\nconst EMPTY_CONTACT: ContactInfo = { phone: '', email: '' }\nconst DEFAULT_PACKAGE: PackageDimension = { weightKg: 1, lengthCm: 20, widthCm: 15, heightCm: 10 }\n\nconst buildAddressFromDocument = (doc: DocumentAddress): Address => ({\n countryCode: doc.country ?? '',\n postalCode: doc.postal_code ?? '',\n city: doc.city ?? '',\n line1: doc.address_line1,\n line2: doc.address_line2 ?? undefined,\n})\n\nconst isAddressValid = (addr: Address) =>\n addr.countryCode.length >= 2 &&\n addr.postalCode.length > 0 &&\n addr.city.length > 0 &&\n addr.line1.length > 0\n\nconst isPackageValid = (pkg: PackageDimension) =>\n pkg.weightKg > 0 && pkg.lengthCm > 0 && pkg.widthCm > 0 && pkg.heightCm > 0\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\nconst PHONE_REGEX = /^[+\\d][\\d\\s\\-().]{6,}$/\n\nconst isEmailValid = (email: string) => email === '' || EMAIL_REGEX.test(email)\nconst isPhoneValid = (phone: string) => phone === '' || PHONE_REGEX.test(phone)\n\nexport type ShipmentWizard = {\n step: WizardStep\n backHref: string\n\n // Provider step\n providers: Provider[]\n isLoadingProviders: boolean\n providerError: string | null\n selectedProvider: string | null\n\n // Configure step\n origin: Address\n destination: Address\n packages: PackageDimension[]\n labelFormat: LabelFormat\n senderContact: ContactInfo\n receiverContact: ContactInfo\n targetPoint: string\n c2cSendingMethod: string\n isFetchingRates: boolean\n canProceedFromConfigure: boolean\n senderContactErrors: { email: string | null; phone: string | null }\n receiverContactErrors: { email: string | null; phone: string | null }\n\n // Drop-off point search\n dropOffPointQuery: string\n dropOffPoints: DropOffPoint[]\n isFetchingDropOffPoints: boolean\n dropOffPointsError: string | null\n\n // Confirm step\n rates: ShippingRate[]\n ratesError: string | null\n selectedRate: ShippingRate | null\n isSubmitting: boolean\n\n // Actions\n goToStep: (step: WizardStep) => void\n handleProviderSelect: (providerKey: string) => void\n handleConfigureNext: () => void\n handleSubmit: () => void\n setOrigin: (address: Address) => void\n setDestination: (address: Address) => void\n setPackages: (packages: PackageDimension[]) => void\n setLabelFormat: (format: LabelFormat) => void\n setSenderContact: (contact: ContactInfo) => void\n setReceiverContact: (contact: ContactInfo) => void\n setTargetPoint: (point: string) => void\n setC2cSendingMethod: (method: string) => void\n setSelectedRate: (rate: ShippingRate) => void\n searchDropOffPoints: (query: string) => void\n}\n\nexport const useShipmentWizard = (): ShipmentWizard => {\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const orderId = searchParams?.get('orderId') ?? null\n\n const [step, setStep] = React.useState<WizardStep>('provider')\n const [providers, setProviders] = React.useState<Provider[]>([])\n const [isLoadingProviders, setIsLoadingProviders] = React.useState(true)\n const [providerError, setProviderError] = React.useState<string | null>(null)\n const [selectedProvider, setSelectedProvider] = React.useState<string | null>(null)\n\n const [origin, setOrigin] = React.useState<Address>(EMPTY_ADDRESS)\n const [destination, setDestination] = React.useState<Address>(EMPTY_ADDRESS)\n const [packages, setPackages] = React.useState<PackageDimension[]>([DEFAULT_PACKAGE])\n const [labelFormat, setLabelFormat] = React.useState<LabelFormat>('pdf')\n const [senderContact, setSenderContact] = React.useState<ContactInfo>(EMPTY_CONTACT)\n const [receiverContact, setReceiverContact] = React.useState<ContactInfo>(EMPTY_CONTACT)\n const [targetPoint, setTargetPoint] = React.useState<string>('')\n const [c2cSendingMethod, setC2cSendingMethod] = React.useState<string>('')\n\n const [dropOffPointQuery, setDropOffPointQuery] = React.useState<string>('')\n const [dropOffPoints, setDropOffPoints] = React.useState<DropOffPoint[]>([])\n const [isFetchingDropOffPoints, setIsFetchingDropOffPoints] = React.useState(false)\n const [dropOffPointsError, setDropOffPointsError] = React.useState<string | null>(null)\n\n const [rates, setRates] = React.useState<ShippingRate[]>([])\n const [isFetchingRates, setIsFetchingRates] = React.useState(false)\n const [ratesError, setRatesError] = React.useState<string | null>(null)\n const [selectedRate, setSelectedRate] = React.useState<ShippingRate | null>(null)\n\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const backHref = orderId ? `/backend/sales/orders/${orderId}` : '/backend/sales/orders'\n\n React.useEffect(() => {\n const loadProviders = async () => {\n setIsLoadingProviders(true)\n setProviderError(null)\n const result = await fetchProviders()\n if (result.ok) {\n setProviders(result.providers)\n } else {\n setProviderError(t('shipping_carriers.create.error.loadProviders', result.error))\n }\n setIsLoadingProviders(false)\n }\n void loadProviders()\n }, [t])\n\n React.useEffect(() => {\n if (!orderId) return\n const prefillAddresses = async () => {\n const result = await fetchOrderAddresses(orderId)\n if (!result.ok) return\n const items = result.items\n const shippingAddr =\n items.find((item) => item.purpose === 'shipping') ??\n items.find((item) => item.purpose === 'delivery') ??\n items[0]\n if (shippingAddr) setDestination(buildAddressFromDocument(shippingAddr))\n }\n void prefillAddresses()\n }, [orderId])\n\n const loadRates = async () => {\n if (!selectedProvider) return\n setIsFetchingRates(true)\n setRatesError(null)\n setRates([])\n setSelectedRate(null)\n const result = await fetchRates({\n providerKey: selectedProvider,\n origin,\n destination,\n packages,\n ...(receiverContact.phone ? { receiverPhone: receiverContact.phone } : {}),\n ...(receiverContact.email ? { receiverEmail: receiverContact.email } : {}),\n })\n if (result.ok) {\n setRates(result.rates)\n if (result.rates.length > 0) setSelectedRate(result.rates[0])\n } else {\n setRatesError(t('shipping_carriers.create.error.fetchRates', result.error))\n }\n setIsFetchingRates(false)\n }\n\n const handleSubmit = async () => {\n if (!selectedProvider || !selectedRate || !orderId) return\n setIsSubmitting(true)\n const result = await createShipment({\n providerKey: selectedProvider,\n orderId,\n origin,\n destination,\n packages,\n serviceCode: selectedRate.serviceCode,\n labelFormat,\n ...(senderContact.phone ? { senderPhone: senderContact.phone } : {}),\n ...(senderContact.email ? { senderEmail: senderContact.email } : {}),\n ...(receiverContact.phone ? { receiverPhone: receiverContact.phone } : {}),\n ...(receiverContact.email ? { receiverEmail: receiverContact.email } : {}),\n ...(targetPoint ? { targetPoint } : {}),\n ...(c2cSendingMethod ? { c2cSendingMethod } : {}),\n })\n setIsSubmitting(false)\n if (result.ok) {\n flash(t('shipping_carriers.create.success', 'Shipment created successfully.'), 'success')\n router.push(backHref)\n } else {\n flash(t('shipping_carriers.create.error.create', result.error), 'error')\n }\n }\n\n const goToStep = (next: WizardStep) => setStep(next)\n\n const handleProviderSelect = (providerKey: string) => {\n setSelectedProvider(providerKey)\n goToStep('configure')\n }\n\n const handleConfigureNext = () => {\n void loadRates().then(() => goToStep('confirm'))\n }\n\n const searchDropOffPoints = async (query: string) => {\n if (!selectedProvider) return\n setDropOffPointQuery(query)\n setIsFetchingDropOffPoints(true)\n setDropOffPointsError(null)\n const isPostCode = /^\\d{2}-?\\d{3}$/.test(query.trim())\n const resolvedType = c2cSendingMethod === 'pop' ? 'pop' : 'parcel_locker'\n const result = await fetchDropOffPoints({\n providerKey: selectedProvider,\n ...(isPostCode ? { postCode: query.trim() } : { query: query.trim() }),\n type: resolvedType,\n })\n if (result.ok) {\n setDropOffPoints(result.points)\n } else {\n setDropOffPointsError(t('shipping_carriers.create.error.fetchDropOffPoints', result.error))\n setDropOffPoints([])\n }\n setIsFetchingDropOffPoints(false)\n }\n\n const canProceedFromConfigure =\n isAddressValid(origin) &&\n isAddressValid(destination) &&\n packages.length > 0 &&\n packages.every(isPackageValid) &&\n isEmailValid(senderContact.email) &&\n isPhoneValid(senderContact.phone) &&\n isEmailValid(receiverContact.email) &&\n isPhoneValid(receiverContact.phone)\n\n const senderContactErrors = {\n email: !isEmailValid(senderContact.email) ? t('shipping_carriers.create.error.invalidEmail', 'Invalid email address.') : null,\n phone: !isPhoneValid(senderContact.phone) ? t('shipping_carriers.create.error.invalidPhone', 'Invalid phone number.') : null,\n }\n const receiverContactErrors = {\n email: !isEmailValid(receiverContact.email) ? t('shipping_carriers.create.error.invalidEmail', 'Invalid email address.') : null,\n phone: !isPhoneValid(receiverContact.phone) ? t('shipping_carriers.create.error.invalidPhone', 'Invalid phone number.') : null,\n }\n\n return {\n step,\n backHref,\n providers,\n isLoadingProviders,\n providerError,\n selectedProvider,\n origin,\n destination,\n packages,\n labelFormat,\n senderContact,\n receiverContact,\n targetPoint,\n c2cSendingMethod,\n isFetchingRates,\n canProceedFromConfigure,\n senderContactErrors,\n receiverContactErrors,\n dropOffPointQuery,\n dropOffPoints,\n isFetchingDropOffPoints,\n dropOffPointsError,\n rates,\n ratesError,\n selectedRate,\n isSubmitting,\n goToStep,\n handleProviderSelect,\n handleConfigureNext,\n handleSubmit: () => void handleSubmit(),\n setOrigin,\n setDestination,\n setPackages,\n setLabelFormat,\n setSenderContact,\n setReceiverContact,\n setTargetPoint,\n setC2cSendingMethod,\n setSelectedRate,\n searchDropOffPoints: (query: string) => void searchDropOffPoints(query),\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -2,6 +2,7 @@ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find"
2
2
  import { CarrierShipment } from "../data/entities.js";
3
3
  import { emitShippingEvent } from "../events.js";
4
4
  import { getShippingAdapter } from "./adapter-registry.js";
5
+ import { isValidShippingTransition, ShipmentCancelNotAllowedError } from "./status-sync.js";
5
6
  function createShippingCarrierService(deps) {
6
7
  const { em, integrationCredentialsService } = deps;
7
8
  async function findShipmentOrThrow(shipmentId, scope) {
@@ -34,11 +35,16 @@ function createShippingCarrierService(deps) {
34
35
  organizationId: input.organizationId,
35
36
  tenantId: input.tenantId
36
37
  });
38
+ const mergedCredentials = {
39
+ ...credentials,
40
+ ...input.receiverPhone !== void 0 ? { receiverPhone: input.receiverPhone } : {},
41
+ ...input.receiverEmail !== void 0 ? { receiverEmail: input.receiverEmail } : {}
42
+ };
37
43
  return adapter.calculateRates({
38
44
  origin: input.origin,
39
45
  destination: input.destination,
40
46
  packages: input.packages,
41
- credentials
47
+ credentials: mergedCredentials
42
48
  });
43
49
  },
44
50
  async createShipment(input) {
@@ -46,13 +52,22 @@ function createShippingCarrierService(deps) {
46
52
  organizationId: input.organizationId,
47
53
  tenantId: input.tenantId
48
54
  });
55
+ const mergedCredentials = {
56
+ ...credentials,
57
+ ...input.senderPhone !== void 0 ? { senderPhone: input.senderPhone } : {},
58
+ ...input.senderEmail !== void 0 ? { senderEmail: input.senderEmail } : {},
59
+ ...input.receiverPhone !== void 0 ? { receiverPhone: input.receiverPhone } : {},
60
+ ...input.receiverEmail !== void 0 ? { receiverEmail: input.receiverEmail } : {},
61
+ ...input.targetPoint !== void 0 ? { targetPoint: input.targetPoint } : {},
62
+ ...input.c2cSendingMethod !== void 0 ? { c2cSendingMethod: input.c2cSendingMethod } : {}
63
+ };
49
64
  const created = await adapter.createShipment({
50
65
  orderId: input.orderId,
51
66
  origin: input.origin,
52
67
  destination: input.destination,
53
68
  packages: input.packages,
54
69
  serviceCode: input.serviceCode,
55
- credentials,
70
+ credentials: mergedCredentials,
56
71
  labelFormat: input.labelFormat
57
72
  });
58
73
  const shipment = em.create(CarrierShipment, {
@@ -99,7 +114,7 @@ function createShippingCarrierService(deps) {
99
114
  ) : null;
100
115
  const tracking = await adapter.getTracking({
101
116
  shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,
102
- trackingNumber: input.trackingNumber,
117
+ trackingNumber: input.trackingNumber ?? shipment?.trackingNumber,
103
118
  credentials
104
119
  });
105
120
  if (shipment) {
@@ -113,6 +128,9 @@ function createShippingCarrierService(deps) {
113
128
  async cancelShipment(input) {
114
129
  const scope = { organizationId: input.organizationId, tenantId: input.tenantId };
115
130
  const shipment = await findShipmentOrThrow(input.shipmentId, scope);
131
+ if (!isValidShippingTransition(shipment.unifiedStatus, "cancelled")) {
132
+ throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus);
133
+ }
116
134
  const { adapter, credentials } = await resolveAdapter(input.providerKey, {
117
135
  organizationId: input.organizationId,
118
136
  tenantId: input.tenantId
@@ -146,6 +164,21 @@ function createShippingCarrierService(deps) {
146
164
  void 0,
147
165
  scope
148
166
  );
167
+ },
168
+ async searchDropOffPoints(input) {
169
+ const { adapter, credentials } = await resolveAdapter(input.providerKey, {
170
+ organizationId: input.organizationId,
171
+ tenantId: input.tenantId
172
+ });
173
+ if (!adapter.searchDropOffPoints) {
174
+ throw new Error(`Provider ${input.providerKey} does not support drop-off point search`);
175
+ }
176
+ return adapter.searchDropOffPoints({
177
+ query: input.query,
178
+ type: input.type,
179
+ postCode: input.postCode,
180
+ credentials
181
+ });
149
182
  }
150
183
  };
151
184
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/lib/shipping-service.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new Error('Shipment not found')\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials,\n labelFormat: input.labelFormat,\n })\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persistAndFlush(shipment)\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return shipment\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const shipment = input.shipmentId\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: input.shipmentId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n },\n )\n : null\n const tracking = await adapter.getTracking({\n shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,\n trackingNumber: input.trackingNumber,\n credentials,\n })\n if (shipment) {\n shipment.unifiedStatus = tracking.status\n shipment.trackingEvents = tracking.events\n shipment.lastPolledAt = new Date()\n await em.flush()\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\nimport type { UnifiedShipmentStatus } from './adapter'\nimport { isValidShippingTransition, ShipmentCancelNotAllowedError } from './status-sync'\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new Error('Shipment not found')\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n receiverPhone?: string\n receiverEmail?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n }\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials: mergedCredentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.senderPhone !== undefined ? { senderPhone: input.senderPhone } : {}),\n ...(input.senderEmail !== undefined ? { senderEmail: input.senderEmail } : {}),\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n ...(input.targetPoint !== undefined ? { targetPoint: input.targetPoint } : {}),\n ...(input.c2cSendingMethod !== undefined ? { c2cSendingMethod: input.c2cSendingMethod } : {}),\n }\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials: mergedCredentials,\n labelFormat: input.labelFormat,\n })\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persistAndFlush(shipment)\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return shipment\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const shipment = input.shipmentId\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: input.shipmentId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n },\n )\n : null\n const tracking = await adapter.getTracking({\n shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,\n trackingNumber: input.trackingNumber ?? shipment?.trackingNumber,\n credentials,\n })\n if (shipment) {\n shipment.unifiedStatus = tracking.status\n shipment.trackingEvents = tracking.events\n shipment.lastPolledAt = new Date()\n await em.flush()\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n if (!isValidShippingTransition(shipment.unifiedStatus as UnifiedShipmentStatus, 'cancelled')) {\n throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus)\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async searchDropOffPoints(input: {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n if (!adapter.searchDropOffPoints) {\n throw new Error(`Provider ${input.providerKey} does not support drop-off point search`)\n }\n return adapter.searchDropOffPoints({\n query: input.query,\n type: input.type,\n postCode: input.postCode,\n credentials,\n })\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,9 @@
1
+ class ShipmentCancelNotAllowedError extends Error {
2
+ constructor(status) {
3
+ super(`Shipment cannot be cancelled in its current status: ${status}`);
4
+ this.name = "ShipmentCancelNotAllowedError";
5
+ }
6
+ }
1
7
  const VALID_SHIPPING_TRANSITIONS = {
2
8
  label_created: ["picked_up", "in_transit", "cancelled"],
3
9
  picked_up: ["in_transit", "cancelled"],
@@ -29,6 +35,7 @@ function getTerminalShippingEvent(status) {
29
35
  return null;
30
36
  }
31
37
  export {
38
+ ShipmentCancelNotAllowedError,
32
39
  TERMINAL_SHIPPING_STATUSES,
33
40
  getTerminalShippingEvent,
34
41
  isValidShippingTransition,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/lib/status-sync.ts"],
4
- "sourcesContent": ["import type { UnifiedShipmentStatus } from './adapter'\nimport type { CarrierShipment } from '../data/entities'\nimport type { ShippingEventId } from '../events'\n\nconst VALID_SHIPPING_TRANSITIONS: Record<string, UnifiedShipmentStatus[]> = {\n label_created: ['picked_up', 'in_transit', 'cancelled'],\n picked_up: ['in_transit', 'cancelled'],\n in_transit: ['out_for_delivery', 'delivered', 'returned', 'failed_delivery'],\n out_for_delivery: ['delivered', 'returned', 'failed_delivery'],\n failed_delivery: ['in_transit', 'out_for_delivery', 'delivered', 'returned', 'cancelled'],\n}\n\nexport const TERMINAL_SHIPPING_STATUSES: Set<UnifiedShipmentStatus> = new Set([\n 'delivered',\n 'returned',\n 'cancelled',\n])\n\nexport function isValidShippingTransition(from: UnifiedShipmentStatus, to: UnifiedShipmentStatus): boolean {\n if (from === to) return false\n const allowed = VALID_SHIPPING_TRANSITIONS[from]\n if (!allowed) return false\n return allowed.includes(to)\n}\n\nexport function syncShipmentStatus(shipment: CarrierShipment, newStatus: UnifiedShipmentStatus): boolean {\n const currentStatus = shipment.unifiedStatus as UnifiedShipmentStatus\n if (!isValidShippingTransition(currentStatus, newStatus)) return false\n shipment.unifiedStatus = newStatus\n return true\n}\n\nexport function getTerminalShippingEvent(status: UnifiedShipmentStatus): ShippingEventId | null {\n if (status === 'delivered') return 'shipping_carriers.shipment.delivered'\n if (status === 'returned') return 'shipping_carriers.shipment.returned'\n if (status === 'cancelled') return 'shipping_carriers.shipment.cancelled'\n return null\n}\n"],
5
- "mappings": "AAIA,MAAM,6BAAsE;AAAA,EAC1E,eAAe,CAAC,aAAa,cAAc,WAAW;AAAA,EACtD,WAAW,CAAC,cAAc,WAAW;AAAA,EACrC,YAAY,CAAC,oBAAoB,aAAa,YAAY,iBAAiB;AAAA,EAC3E,kBAAkB,CAAC,aAAa,YAAY,iBAAiB;AAAA,EAC7D,iBAAiB,CAAC,cAAc,oBAAoB,aAAa,YAAY,WAAW;AAC1F;AAEO,MAAM,6BAAyD,oBAAI,IAAI;AAAA,EAC5E;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,0BAA0B,MAA6B,IAAoC;AACzG,MAAI,SAAS,GAAI,QAAO;AACxB,QAAM,UAAU,2BAA2B,IAAI;AAC/C,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,SAAS,EAAE;AAC5B;AAEO,SAAS,mBAAmB,UAA2B,WAA2C;AACvG,QAAM,gBAAgB,SAAS;AAC/B,MAAI,CAAC,0BAA0B,eAAe,SAAS,EAAG,QAAO;AACjE,WAAS,gBAAgB;AACzB,SAAO;AACT;AAEO,SAAS,yBAAyB,QAAuD;AAC9F,MAAI,WAAW,YAAa,QAAO;AACnC,MAAI,WAAW,WAAY,QAAO;AAClC,MAAI,WAAW,YAAa,QAAO;AACnC,SAAO;AACT;",
4
+ "sourcesContent": ["import type { UnifiedShipmentStatus } from './adapter'\nimport type { CarrierShipment } from '../data/entities'\nimport type { ShippingEventId } from '../events'\n\nexport class ShipmentCancelNotAllowedError extends Error {\n constructor(status: string) {\n super(`Shipment cannot be cancelled in its current status: ${status}`)\n this.name = 'ShipmentCancelNotAllowedError'\n }\n}\n\nconst VALID_SHIPPING_TRANSITIONS: Record<string, UnifiedShipmentStatus[]> = {\n label_created: ['picked_up', 'in_transit', 'cancelled'],\n picked_up: ['in_transit', 'cancelled'],\n in_transit: ['out_for_delivery', 'delivered', 'returned', 'failed_delivery'],\n out_for_delivery: ['delivered', 'returned', 'failed_delivery'],\n failed_delivery: ['in_transit', 'out_for_delivery', 'delivered', 'returned', 'cancelled'],\n}\n\nexport const TERMINAL_SHIPPING_STATUSES: Set<UnifiedShipmentStatus> = new Set([\n 'delivered',\n 'returned',\n 'cancelled',\n])\n\nexport function isValidShippingTransition(from: UnifiedShipmentStatus, to: UnifiedShipmentStatus): boolean {\n if (from === to) return false\n const allowed = VALID_SHIPPING_TRANSITIONS[from]\n if (!allowed) return false\n return allowed.includes(to)\n}\n\nexport function syncShipmentStatus(shipment: CarrierShipment, newStatus: UnifiedShipmentStatus): boolean {\n const currentStatus = shipment.unifiedStatus as UnifiedShipmentStatus\n if (!isValidShippingTransition(currentStatus, newStatus)) return false\n shipment.unifiedStatus = newStatus\n return true\n}\n\nexport function getTerminalShippingEvent(status: UnifiedShipmentStatus): ShippingEventId | null {\n if (status === 'delivered') return 'shipping_carriers.shipment.delivered'\n if (status === 'returned') return 'shipping_carriers.shipment.returned'\n if (status === 'cancelled') return 'shipping_carriers.shipment.cancelled'\n return null\n}\n"],
5
+ "mappings": "AAIO,MAAM,sCAAsC,MAAM;AAAA,EACvD,YAAY,QAAgB;AAC1B,UAAM,uDAAuD,MAAM,EAAE;AACrE,SAAK,OAAO;AAAA,EACd;AACF;AAEA,MAAM,6BAAsE;AAAA,EAC1E,eAAe,CAAC,aAAa,cAAc,WAAW;AAAA,EACtD,WAAW,CAAC,cAAc,WAAW;AAAA,EACrC,YAAY,CAAC,oBAAoB,aAAa,YAAY,iBAAiB;AAAA,EAC3E,kBAAkB,CAAC,aAAa,YAAY,iBAAiB;AAAA,EAC7D,iBAAiB,CAAC,cAAc,oBAAoB,aAAa,YAAY,WAAW;AAC1F;AAEO,MAAM,6BAAyD,oBAAI,IAAI;AAAA,EAC5E;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,0BAA0B,MAA6B,IAAoC;AACzG,MAAI,SAAS,GAAI,QAAO;AACxB,QAAM,UAAU,2BAA2B,IAAI;AAC/C,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,SAAS,EAAE;AAC5B;AAEO,SAAS,mBAAmB,UAA2B,WAA2C;AACvG,QAAM,gBAAgB,SAAS;AAC/B,MAAI,CAAC,0BAA0B,eAAe,SAAS,EAAG,QAAO;AACjE,WAAS,gBAAgB;AACzB,SAAO;AACT;AAEO,SAAS,yBAAyB,QAAuD;AAC9F,MAAI,WAAW,YAAa,QAAO;AACnC,MAAI,WAAW,WAAY,QAAO;AAClC,MAAI,WAAW,YAAa,QAAO;AACnC,SAAO;AACT;",
6
6
  "names": []
7
7
  }