@open-mercato/core 0.6.6-develop.5617.1.62538c48ca → 0.6.6-develop.5637.1.7a68607cc6

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 (30) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/sales/acl.js +6 -0
  3. package/dist/modules/sales/acl.js.map +2 -2
  4. package/dist/modules/sales/api/returns/route.js +43 -3
  5. package/dist/modules/sales/api/returns/route.js.map +2 -2
  6. package/dist/modules/sales/commands/returns.js +473 -213
  7. package/dist/modules/sales/commands/returns.js.map +2 -2
  8. package/dist/modules/sales/commands/shared.js +2 -0
  9. package/dist/modules/sales/commands/shared.js.map +2 -2
  10. package/dist/modules/sales/components/documents/ReturnEditDialog.js +126 -0
  11. package/dist/modules/sales/components/documents/ReturnEditDialog.js.map +7 -0
  12. package/dist/modules/sales/components/documents/ReturnsSection.js +102 -6
  13. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  14. package/dist/modules/sales/data/validators.js +19 -1
  15. package/dist/modules/sales/data/validators.js.map +2 -2
  16. package/dist/modules/sales/setup.js +1 -0
  17. package/dist/modules/sales/setup.js.map +2 -2
  18. package/package.json +7 -7
  19. package/src/modules/sales/acl.ts +6 -0
  20. package/src/modules/sales/api/returns/route.ts +41 -3
  21. package/src/modules/sales/commands/returns.ts +561 -229
  22. package/src/modules/sales/commands/shared.ts +1 -0
  23. package/src/modules/sales/components/documents/ReturnEditDialog.tsx +158 -0
  24. package/src/modules/sales/components/documents/ReturnsSection.tsx +105 -3
  25. package/src/modules/sales/data/validators.ts +28 -1
  26. package/src/modules/sales/i18n/de.json +11 -0
  27. package/src/modules/sales/i18n/en.json +11 -0
  28. package/src/modules/sales/i18n/es.json +11 -0
  29. package/src/modules/sales/i18n/pl.json +11 -0
  30. package/src/modules/sales/setup.ts +1 -0
@@ -5,7 +5,13 @@ import { Undo2, Plus } from "lucide-react";
5
5
  import { Button } from "@open-mercato/ui/primitives/button";
6
6
  import { Badge } from "@open-mercato/ui/primitives/badge";
7
7
  import { ErrorMessage, LoadingMessage, TabEmptyState } from "@open-mercato/ui/backend/detail";
8
- import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
8
+ import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
9
+ import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
10
+ import { deleteCrud } from "@open-mercato/ui/backend/utils/crud";
11
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
12
+ import { RowActions } from "@open-mercato/ui/backend/RowActions";
13
+ import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
14
+ import { useOrganizationScopeDetail } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
9
15
  import { useT } from "@open-mercato/shared/lib/i18n/context";
10
16
  import {
11
17
  emitSalesDocumentTotalsRefresh,
@@ -13,6 +19,8 @@ import {
13
19
  } from "@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents";
14
20
  import { formatMoney, normalizeNumber } from "./lineItemUtils.js";
15
21
  import { ReturnDialog } from "./ReturnDialog.js";
22
+ import { ReturnEditDialog } from "./ReturnEditDialog.js";
23
+ import { handleSectionMutationError, readRowUpdatedAt, rowOptimisticVersion } from "./optimisticLock.js";
16
24
  function formatDisplayDate(value) {
17
25
  if (!value) return null;
18
26
  const date = new Date(value);
@@ -21,11 +29,14 @@ function formatDisplayDate(value) {
21
29
  }
22
30
  function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }) {
23
31
  const t = useT();
32
+ const { organizationId, tenantId } = useOrganizationScopeDetail();
33
+ const { confirm, ConfirmDialogElement } = useConfirmDialog();
24
34
  const [returns, setReturns] = React.useState([]);
25
35
  const [lines, setLines] = React.useState([]);
26
36
  const [loading, setLoading] = React.useState(false);
27
37
  const [error, setError] = React.useState(null);
28
38
  const [dialogOpen, setDialogOpen] = React.useState(false);
39
+ const [editRecord, setEditRecord] = React.useState(null);
29
40
  const loadLines = React.useCallback(async () => {
30
41
  const params = new URLSearchParams({ page: "1", pageSize: "100", orderId });
31
42
  const response = await apiCall(
@@ -73,11 +84,16 @@ function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }) {
73
84
  const returnedAt = typeof map["returned_at"] === "string" ? map["returned_at"] : typeof map.returnedAt === "string" ? map.returnedAt : null;
74
85
  const totalNetAmount = typeof map["total_net_amount"] === "number" ? map["total_net_amount"] : typeof map.totalNetAmount === "number" ? map.totalNetAmount : null;
75
86
  const totalGrossAmount = typeof map["total_gross_amount"] === "number" ? map["total_gross_amount"] : typeof map.totalGrossAmount === "number" ? map.totalGrossAmount : null;
87
+ const reason = typeof map.reason === "string" ? map.reason : null;
88
+ const notes = typeof map.notes === "string" ? map.notes : null;
76
89
  return {
77
90
  id,
78
91
  returnNumber,
79
92
  status: typeof map.status === "string" ? map.status : null,
93
+ reason,
94
+ notes,
80
95
  returnedAt,
96
+ updatedAt: readRowUpdatedAt(map),
81
97
  totalNetAmount,
82
98
  totalGrossAmount
83
99
  };
@@ -115,6 +131,50 @@ function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }) {
115
131
  };
116
132
  });
117
133
  }, [returns]);
134
+ const handleEdit = React.useCallback((row) => {
135
+ setEditRecord({
136
+ id: row.id,
137
+ reason: row.reason,
138
+ notes: row.notes,
139
+ returnedAt: row.returnedAt,
140
+ updatedAt: row.updatedAt
141
+ });
142
+ }, []);
143
+ const handleDelete = React.useCallback(
144
+ async (row) => {
145
+ const confirmed = await confirm({
146
+ title: t("sales.returns.confirmDelete", "Delete this return?"),
147
+ description: t(
148
+ "sales.returns.confirmDelete.description",
149
+ "This reverses the returned quantities and the related credit adjustments."
150
+ ),
151
+ variant: "destructive"
152
+ });
153
+ if (!confirmed) return;
154
+ try {
155
+ const result = await withScopedApiRequestHeaders(
156
+ buildOptimisticLockHeader(rowOptimisticVersion(row)),
157
+ () => deleteCrud("sales/returns", {
158
+ body: {
159
+ id: row.id,
160
+ orderId,
161
+ ...organizationId ? { organizationId } : {},
162
+ ...tenantId ? { tenantId } : {}
163
+ },
164
+ errorMessage: t("sales.returns.errors.delete", "Failed to delete return.")
165
+ })
166
+ );
167
+ if (result.ok) {
168
+ emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: "order" });
169
+ await loadReturns();
170
+ }
171
+ } catch (err) {
172
+ if (handleSectionMutationError(err, t, () => void loadReturns())) return;
173
+ flash(t("sales.returns.errors.delete", "Failed to delete return."), "error");
174
+ }
175
+ },
176
+ [confirm, loadReturns, orderId, organizationId, tenantId, t]
177
+ );
118
178
  if (loading) return /* @__PURE__ */ jsx(LoadingMessage, { label: t("sales.returns.loading", "Loading returns\u2026") });
119
179
  if (error) return /* @__PURE__ */ jsx(ErrorMessage, { label: error });
120
180
  if (!rows.length) {
@@ -153,19 +213,38 @@ function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }) {
153
213
  t("sales.returns.create", "Create return")
154
214
  ] }) }),
155
215
  /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md border", children: [
156
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_auto_auto] gap-3 border-b bg-muted/30 px-4 py-2 text-xs font-medium text-muted-foreground", children: [
216
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_auto_auto_auto] gap-3 border-b bg-muted/30 px-4 py-2 text-xs font-medium text-muted-foreground", children: [
157
217
  /* @__PURE__ */ jsx("div", { children: t("sales.returns.returnNumber", "Return") }),
158
218
  /* @__PURE__ */ jsx("div", { className: "text-right", children: t("sales.returns.returnedAt", "Returned at") }),
159
- /* @__PURE__ */ jsx("div", { className: "text-right", children: t("sales.returns.total", "Total") })
219
+ /* @__PURE__ */ jsx("div", { className: "text-right", children: t("sales.returns.total", "Total") }),
220
+ /* @__PURE__ */ jsx("div", { className: "sr-only", children: t("sales.returns.actions", "Actions") })
160
221
  ] }),
161
- /* @__PURE__ */ jsx("div", { className: "divide-y", children: rows.map((ret) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_auto_auto] items-center gap-3 px-4 py-3", children: [
222
+ /* @__PURE__ */ jsx("div", { className: "divide-y", children: rows.map((ret) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_auto_auto_auto] items-center gap-3 px-4 py-3", children: [
162
223
  /* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
163
224
  /* @__PURE__ */ jsx(Undo2, { className: "h-4 w-4 text-muted-foreground", "aria-hidden": true }),
164
225
  /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: ret.returnNumber }),
165
226
  ret.status ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: ret.status }) : null
166
227
  ] }) }),
167
228
  /* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-right text-sm text-muted-foreground", children: formatDisplayDate(ret.returnedAt) ?? t("sales.returns.notSet", "Not set") }),
168
- /* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-right text-sm font-medium", children: formatMoney(ret.total, currencyCode ?? null) })
229
+ /* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-right text-sm font-medium", children: formatMoney(ret.total, currencyCode ?? null) }),
230
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
231
+ RowActions,
232
+ {
233
+ items: [
234
+ {
235
+ id: "edit",
236
+ label: t("ui.actions.edit", "Edit"),
237
+ onSelect: () => handleEdit(ret)
238
+ },
239
+ {
240
+ id: "delete",
241
+ label: t("ui.actions.delete", "Delete"),
242
+ destructive: true,
243
+ onSelect: () => void handleDelete(ret)
244
+ }
245
+ ]
246
+ }
247
+ ) })
169
248
  ] }, ret.id)) })
170
249
  ] }),
171
250
  /* @__PURE__ */ jsx(
@@ -181,7 +260,24 @@ function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }) {
181
260
  await loadReturns();
182
261
  }
183
262
  }
184
- )
263
+ ),
264
+ /* @__PURE__ */ jsx(
265
+ ReturnEditDialog,
266
+ {
267
+ open: editRecord !== null,
268
+ returnRecord: editRecord,
269
+ orderId,
270
+ organizationId: organizationId ?? null,
271
+ tenantId: tenantId ?? null,
272
+ onClose: () => setEditRecord(null),
273
+ onSaved: async () => {
274
+ setEditRecord(null);
275
+ emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: "order" });
276
+ await loadReturns();
277
+ }
278
+ }
279
+ ),
280
+ ConfirmDialogElement
185
281
  ] });
186
282
  }
187
283
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/components/documents/ReturnsSection.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Undo2, Plus } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n emitSalesDocumentTotalsRefresh,\n subscribeSalesDocumentTotalsRefresh,\n} from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'\nimport { formatMoney, normalizeNumber } from './lineItemUtils'\nimport { ReturnDialog, type ReturnOrderLine } from './ReturnDialog'\n\ntype ReturnRow = {\n id: string\n returnNumber: string\n status: string | null\n returnedAt: string | null\n totalNetAmount: number | null\n totalGrossAmount: number | null\n}\n\ntype SalesReturnsSectionProps = {\n orderId: string\n currencyCode?: string | null\n documentUpdatedAt?: string | null\n}\n\nfunction formatDisplayDate(value: string | null | undefined): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(date)\n}\n\nexport function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }: SalesReturnsSectionProps) {\n const t = useT()\n const [returns, setReturns] = React.useState<ReturnRow[]>([])\n const [lines, setLines] = React.useState<ReturnOrderLine[]>([])\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n\n const loadLines = React.useCallback(async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '100', orderId })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/order-lines?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n const items = Array.isArray(response.result?.items) ? response.result?.items ?? [] : []\n const mapped: ReturnOrderLine[] = items\n .map((item) => {\n const map = item as Record<string, unknown>\n const id = typeof map.id === 'string' ? map.id : null\n if (!id) return null\n const snapshot = map['catalog_snapshot']\n const snapshotName =\n snapshot && typeof snapshot === 'object' && !Array.isArray(snapshot)\n ? (snapshot as Record<string, unknown>)['name']\n : null\n const name =\n typeof map.name === 'string'\n ? map.name\n : typeof snapshotName === 'string'\n ? snapshotName\n : null\n const lineNumber =\n typeof map['line_number'] === 'number'\n ? (map['line_number'] as number)\n : typeof map.lineNumber === 'number'\n ? (map.lineNumber as number)\n : null\n const quantity =\n typeof map.quantity === 'number'\n ? (map.quantity as number)\n : typeof map.quantity === 'string'\n ? Number(map.quantity)\n : 0\n const returnedQuantity =\n typeof map['returned_quantity'] === 'number'\n ? (map['returned_quantity'] as number)\n : typeof map.returnedQuantity === 'number'\n ? (map.returnedQuantity as number)\n : typeof map['returned_quantity'] === 'string'\n ? Number(map['returned_quantity'])\n : typeof map.returnedQuantity === 'string'\n ? Number(map.returnedQuantity)\n : 0\n return {\n id,\n title: name ?? id,\n lineNumber,\n quantity: Number.isFinite(quantity) ? quantity : 0,\n returnedQuantity: Number.isFinite(returnedQuantity) ? returnedQuantity : 0,\n }\n })\n .filter((entry): entry is ReturnOrderLine => Boolean(entry?.id))\n setLines(mapped)\n }, [orderId])\n\n const loadReturns = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '100', orderId })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/returns?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n const items = Array.isArray(response.result?.items) ? response.result?.items ?? [] : []\n const mapped: ReturnRow[] = items\n .map((item) => {\n const map = item as Record<string, unknown>\n const id = typeof map.id === 'string' ? map.id : null\n if (!id) return null\n const returnNumber =\n typeof map['return_number'] === 'string'\n ? (map['return_number'] as string)\n : typeof map.returnNumber === 'string'\n ? (map.returnNumber as string)\n : id\n const returnedAt =\n typeof map['returned_at'] === 'string'\n ? (map['returned_at'] as string)\n : typeof map.returnedAt === 'string'\n ? (map.returnedAt as string)\n : null\n const totalNetAmount =\n typeof map['total_net_amount'] === 'number'\n ? (map['total_net_amount'] as number)\n : typeof map.totalNetAmount === 'number'\n ? (map.totalNetAmount as number)\n : null\n const totalGrossAmount =\n typeof map['total_gross_amount'] === 'number'\n ? (map['total_gross_amount'] as number)\n : typeof map.totalGrossAmount === 'number'\n ? (map.totalGrossAmount as number)\n : null\n return {\n id,\n returnNumber,\n status: typeof map.status === 'string' ? (map.status as string) : null,\n returnedAt,\n totalNetAmount,\n totalGrossAmount,\n }\n })\n .filter((entry): entry is ReturnRow => Boolean(entry?.id))\n setReturns(mapped)\n await loadLines()\n } catch {\n setError(t('sales.returns.errors.load', 'Failed to load returns.'))\n } finally {\n setLoading(false)\n }\n }, [loadLines, orderId, t])\n\n React.useEffect(() => {\n loadReturns()\n }, [loadReturns])\n\n React.useEffect(() => {\n return subscribeSalesDocumentTotalsRefresh((detail) => {\n if (detail.documentId !== orderId) return\n loadReturns()\n })\n }, [loadReturns, orderId])\n\n const emptyState = React.useMemo(\n () => ({\n title: t('sales.returns.empty.title', 'No returns yet.'),\n description: t('sales.returns.empty.description', 'Create a return to generate credit adjustments for returned items.'),\n }),\n [t],\n )\n\n const rows = React.useMemo(() => {\n return returns.map((ret) => {\n const total = normalizeNumber(ret.totalGrossAmount ?? ret.totalNetAmount ?? 0, 0)\n return {\n ...ret,\n total,\n }\n })\n }, [returns])\n\n if (loading) return <LoadingMessage label={t('sales.returns.loading', 'Loading returns\u2026')} />\n if (error) return <ErrorMessage label={error} />\n\n if (!rows.length) {\n return (\n <div className=\"space-y-4\">\n <TabEmptyState\n title={emptyState.title}\n description={emptyState.description}\n action={{\n label: t('sales.returns.create', 'Create return'),\n icon: <Plus className=\"mr-2 h-4 w-4\" aria-hidden />,\n onClick: () => setDialogOpen(true),\n }}\n />\n <ReturnDialog\n open={dialogOpen}\n orderId={orderId}\n lines={lines}\n documentUpdatedAt={documentUpdatedAt}\n onClose={() => setDialogOpen(false)}\n onSaved={async () => {\n emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })\n await loadReturns()\n }}\n />\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-end\">\n <Button type=\"button\" onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('sales.returns.create', 'Create return')}\n </Button>\n </div>\n\n <div className=\"overflow-hidden rounded-md border\">\n <div className=\"grid grid-cols-[1fr_auto_auto] gap-3 border-b bg-muted/30 px-4 py-2 text-xs font-medium text-muted-foreground\">\n <div>{t('sales.returns.returnNumber', 'Return')}</div>\n <div className=\"text-right\">{t('sales.returns.returnedAt', 'Returned at')}</div>\n <div className=\"text-right\">{t('sales.returns.total', 'Total')}</div>\n </div>\n <div className=\"divide-y\">\n {rows.map((ret) => (\n <div key={ret.id} className=\"grid grid-cols-[1fr_auto_auto] items-center gap-3 px-4 py-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <Undo2 className=\"h-4 w-4 text-muted-foreground\" aria-hidden />\n <div className=\"truncate text-sm font-medium\">{ret.returnNumber}</div>\n {ret.status ? <Badge variant=\"secondary\">{ret.status}</Badge> : null}\n </div>\n </div>\n <div className=\"whitespace-nowrap text-right text-sm text-muted-foreground\">\n {formatDisplayDate(ret.returnedAt) ?? t('sales.returns.notSet', 'Not set')}\n </div>\n <div className=\"whitespace-nowrap text-right text-sm font-medium\">\n {formatMoney(ret.total, currencyCode ?? null)}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n <ReturnDialog\n open={dialogOpen}\n orderId={orderId}\n lines={lines}\n documentUpdatedAt={documentUpdatedAt}\n onClose={() => setDialogOpen(false)}\n onSaved={async () => {\n emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })\n await loadReturns()\n }}\n />\n </div>\n )\n}\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Undo2, Plus } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n emitSalesDocumentTotalsRefresh,\n subscribeSalesDocumentTotalsRefresh,\n} from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'\nimport { formatMoney, normalizeNumber } from './lineItemUtils'\nimport { ReturnDialog, type ReturnOrderLine } from './ReturnDialog'\nimport { ReturnEditDialog, type ReturnEditRecord } from './ReturnEditDialog'\nimport { handleSectionMutationError, readRowUpdatedAt, rowOptimisticVersion } from './optimisticLock'\n\ntype ReturnRow = {\n id: string\n returnNumber: string\n status: string | null\n reason: string | null\n notes: string | null\n returnedAt: string | null\n updatedAt: string | null\n totalNetAmount: number | null\n totalGrossAmount: number | null\n}\n\ntype SalesReturnsSectionProps = {\n orderId: string\n currencyCode?: string | null\n documentUpdatedAt?: string | null\n}\n\nfunction formatDisplayDate(value: string | null | undefined): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(date)\n}\n\nexport function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }: SalesReturnsSectionProps) {\n const t = useT()\n const { organizationId, tenantId } = useOrganizationScopeDetail()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [returns, setReturns] = React.useState<ReturnRow[]>([])\n const [lines, setLines] = React.useState<ReturnOrderLine[]>([])\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [editRecord, setEditRecord] = React.useState<ReturnEditRecord | null>(null)\n\n const loadLines = React.useCallback(async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '100', orderId })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/order-lines?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n const items = Array.isArray(response.result?.items) ? response.result?.items ?? [] : []\n const mapped: ReturnOrderLine[] = items\n .map((item) => {\n const map = item as Record<string, unknown>\n const id = typeof map.id === 'string' ? map.id : null\n if (!id) return null\n const snapshot = map['catalog_snapshot']\n const snapshotName =\n snapshot && typeof snapshot === 'object' && !Array.isArray(snapshot)\n ? (snapshot as Record<string, unknown>)['name']\n : null\n const name =\n typeof map.name === 'string'\n ? map.name\n : typeof snapshotName === 'string'\n ? snapshotName\n : null\n const lineNumber =\n typeof map['line_number'] === 'number'\n ? (map['line_number'] as number)\n : typeof map.lineNumber === 'number'\n ? (map.lineNumber as number)\n : null\n const quantity =\n typeof map.quantity === 'number'\n ? (map.quantity as number)\n : typeof map.quantity === 'string'\n ? Number(map.quantity)\n : 0\n const returnedQuantity =\n typeof map['returned_quantity'] === 'number'\n ? (map['returned_quantity'] as number)\n : typeof map.returnedQuantity === 'number'\n ? (map.returnedQuantity as number)\n : typeof map['returned_quantity'] === 'string'\n ? Number(map['returned_quantity'])\n : typeof map.returnedQuantity === 'string'\n ? Number(map.returnedQuantity)\n : 0\n return {\n id,\n title: name ?? id,\n lineNumber,\n quantity: Number.isFinite(quantity) ? quantity : 0,\n returnedQuantity: Number.isFinite(returnedQuantity) ? returnedQuantity : 0,\n }\n })\n .filter((entry): entry is ReturnOrderLine => Boolean(entry?.id))\n setLines(mapped)\n }, [orderId])\n\n const loadReturns = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '100', orderId })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/returns?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n const items = Array.isArray(response.result?.items) ? response.result?.items ?? [] : []\n const mapped: ReturnRow[] = items\n .map((item) => {\n const map = item as Record<string, unknown>\n const id = typeof map.id === 'string' ? map.id : null\n if (!id) return null\n const returnNumber =\n typeof map['return_number'] === 'string'\n ? (map['return_number'] as string)\n : typeof map.returnNumber === 'string'\n ? (map.returnNumber as string)\n : id\n const returnedAt =\n typeof map['returned_at'] === 'string'\n ? (map['returned_at'] as string)\n : typeof map.returnedAt === 'string'\n ? (map.returnedAt as string)\n : null\n const totalNetAmount =\n typeof map['total_net_amount'] === 'number'\n ? (map['total_net_amount'] as number)\n : typeof map.totalNetAmount === 'number'\n ? (map.totalNetAmount as number)\n : null\n const totalGrossAmount =\n typeof map['total_gross_amount'] === 'number'\n ? (map['total_gross_amount'] as number)\n : typeof map.totalGrossAmount === 'number'\n ? (map.totalGrossAmount as number)\n : null\n const reason =\n typeof map.reason === 'string' ? (map.reason as string) : null\n const notes =\n typeof map.notes === 'string' ? (map.notes as string) : null\n return {\n id,\n returnNumber,\n status: typeof map.status === 'string' ? (map.status as string) : null,\n reason,\n notes,\n returnedAt,\n updatedAt: readRowUpdatedAt(map),\n totalNetAmount,\n totalGrossAmount,\n }\n })\n .filter((entry): entry is ReturnRow => Boolean(entry?.id))\n setReturns(mapped)\n await loadLines()\n } catch {\n setError(t('sales.returns.errors.load', 'Failed to load returns.'))\n } finally {\n setLoading(false)\n }\n }, [loadLines, orderId, t])\n\n React.useEffect(() => {\n loadReturns()\n }, [loadReturns])\n\n React.useEffect(() => {\n return subscribeSalesDocumentTotalsRefresh((detail) => {\n if (detail.documentId !== orderId) return\n loadReturns()\n })\n }, [loadReturns, orderId])\n\n const emptyState = React.useMemo(\n () => ({\n title: t('sales.returns.empty.title', 'No returns yet.'),\n description: t('sales.returns.empty.description', 'Create a return to generate credit adjustments for returned items.'),\n }),\n [t],\n )\n\n const rows = React.useMemo(() => {\n return returns.map((ret) => {\n const total = normalizeNumber(ret.totalGrossAmount ?? ret.totalNetAmount ?? 0, 0)\n return {\n ...ret,\n total,\n }\n })\n }, [returns])\n\n const handleEdit = React.useCallback((row: ReturnRow) => {\n setEditRecord({\n id: row.id,\n reason: row.reason,\n notes: row.notes,\n returnedAt: row.returnedAt,\n updatedAt: row.updatedAt,\n })\n }, [])\n\n const handleDelete = React.useCallback(\n async (row: ReturnRow) => {\n const confirmed = await confirm({\n title: t('sales.returns.confirmDelete', 'Delete this return?'),\n description: t(\n 'sales.returns.confirmDelete.description',\n 'This reverses the returned quantities and the related credit adjustments.',\n ),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const result = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(rowOptimisticVersion(row)),\n () =>\n deleteCrud('sales/returns', {\n body: {\n id: row.id,\n orderId,\n ...(organizationId ? { organizationId } : {}),\n ...(tenantId ? { tenantId } : {}),\n },\n errorMessage: t('sales.returns.errors.delete', 'Failed to delete return.'),\n }),\n )\n if (result.ok) {\n emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })\n await loadReturns()\n }\n } catch (err) {\n if (handleSectionMutationError(err, t, () => void loadReturns())) return\n flash(t('sales.returns.errors.delete', 'Failed to delete return.'), 'error')\n }\n },\n [confirm, loadReturns, orderId, organizationId, tenantId, t],\n )\n\n if (loading) return <LoadingMessage label={t('sales.returns.loading', 'Loading returns\u2026')} />\n if (error) return <ErrorMessage label={error} />\n\n if (!rows.length) {\n return (\n <div className=\"space-y-4\">\n <TabEmptyState\n title={emptyState.title}\n description={emptyState.description}\n action={{\n label: t('sales.returns.create', 'Create return'),\n icon: <Plus className=\"mr-2 h-4 w-4\" aria-hidden />,\n onClick: () => setDialogOpen(true),\n }}\n />\n <ReturnDialog\n open={dialogOpen}\n orderId={orderId}\n lines={lines}\n documentUpdatedAt={documentUpdatedAt}\n onClose={() => setDialogOpen(false)}\n onSaved={async () => {\n emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })\n await loadReturns()\n }}\n />\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-end\">\n <Button type=\"button\" onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('sales.returns.create', 'Create return')}\n </Button>\n </div>\n\n <div className=\"overflow-hidden rounded-md border\">\n <div className=\"grid grid-cols-[1fr_auto_auto_auto] gap-3 border-b bg-muted/30 px-4 py-2 text-xs font-medium text-muted-foreground\">\n <div>{t('sales.returns.returnNumber', 'Return')}</div>\n <div className=\"text-right\">{t('sales.returns.returnedAt', 'Returned at')}</div>\n <div className=\"text-right\">{t('sales.returns.total', 'Total')}</div>\n <div className=\"sr-only\">{t('sales.returns.actions', 'Actions')}</div>\n </div>\n <div className=\"divide-y\">\n {rows.map((ret) => (\n <div key={ret.id} className=\"grid grid-cols-[1fr_auto_auto_auto] items-center gap-3 px-4 py-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <Undo2 className=\"h-4 w-4 text-muted-foreground\" aria-hidden />\n <div className=\"truncate text-sm font-medium\">{ret.returnNumber}</div>\n {ret.status ? <Badge variant=\"secondary\">{ret.status}</Badge> : null}\n </div>\n </div>\n <div className=\"whitespace-nowrap text-right text-sm text-muted-foreground\">\n {formatDisplayDate(ret.returnedAt) ?? t('sales.returns.notSet', 'Not set')}\n </div>\n <div className=\"whitespace-nowrap text-right text-sm font-medium\">\n {formatMoney(ret.total, currencyCode ?? null)}\n </div>\n <div className=\"flex justify-end\">\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('ui.actions.edit', 'Edit'),\n onSelect: () => handleEdit(ret),\n },\n {\n id: 'delete',\n label: t('ui.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => void handleDelete(ret),\n },\n ]}\n />\n </div>\n </div>\n ))}\n </div>\n </div>\n\n <ReturnDialog\n open={dialogOpen}\n orderId={orderId}\n lines={lines}\n documentUpdatedAt={documentUpdatedAt}\n onClose={() => setDialogOpen(false)}\n onSaved={async () => {\n emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })\n await loadReturns()\n }}\n />\n\n <ReturnEditDialog\n open={editRecord !== null}\n returnRecord={editRecord}\n orderId={orderId}\n organizationId={organizationId ?? null}\n tenantId={tenantId ?? null}\n onClose={() => setEditRecord(null)}\n onSaved={async () => {\n setEditRecord(null)\n emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })\n await loadReturns()\n }}\n />\n\n {ConfirmDialogElement}\n </div>\n )\n}\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -569,11 +569,15 @@ const shipmentUpdateSchema = z.object({
569
569
  id: uuid()
570
570
  }).merge(shipmentCreateSchema.partial());
571
571
  const returnLineQuantitySchema = z.coerce.number().int("Return quantity must be a whole number.").min(1, "Return quantity must be at least 1.").max(MAX_QUANTITY, "Quantity is too large.");
572
+ const RETURN_DATE_IN_FUTURE_MESSAGE = "Return date cannot be in the future.";
573
+ const returnedAtSchema = z.coerce.date().refine((value) => value.getTime() <= Date.now(), {
574
+ message: RETURN_DATE_IN_FUTURE_MESSAGE
575
+ }).optional();
572
576
  const returnCreateSchema = scoped.extend({
573
577
  orderId: uuid(),
574
578
  reason: z.string().trim().max(4e3).optional(),
575
579
  notes: z.string().trim().max(4e3).optional(),
576
- returnedAt: z.coerce.date().optional(),
580
+ returnedAt: returnedAtSchema,
577
581
  lines: z.array(
578
582
  z.object({
579
583
  orderLineId: uuid(),
@@ -581,6 +585,17 @@ const returnCreateSchema = scoped.extend({
581
585
  })
582
586
  ).min(1)
583
587
  });
588
+ const returnUpdateSchema = scoped.extend({
589
+ id: uuid(),
590
+ orderId: uuid(),
591
+ reason: z.string().trim().max(4e3).optional(),
592
+ notes: z.string().trim().max(4e3).optional(),
593
+ returnedAt: returnedAtSchema
594
+ });
595
+ const returnDeleteSchema = scoped.extend({
596
+ id: uuid(),
597
+ orderId: uuid()
598
+ });
584
599
  const invoiceCreateSchema = scoped.extend({
585
600
  orderId: uuid().optional(),
586
601
  invoiceNumber: z.string().trim().min(1).max(191).optional(),
@@ -736,6 +751,7 @@ export {
736
751
  RETURN_ADJUSTMENT_EXCEEDS_REMAINING_NET_MESSAGE,
737
752
  RETURN_ADJUSTMENT_POSITIVE_GROSS_MESSAGE,
738
753
  RETURN_ADJUSTMENT_POSITIVE_NET_MESSAGE,
754
+ RETURN_DATE_IN_FUTURE_MESSAGE,
739
755
  SALES_PHONE_INVALID_MESSAGE_KEY,
740
756
  SHIPPING_ADJUSTMENT_NEGATIVE_GROSS_MESSAGE,
741
757
  SHIPPING_ADJUSTMENT_NEGATIVE_NET_MESSAGE,
@@ -779,6 +795,8 @@ export {
779
795
  quoteSendSchema,
780
796
  quoteUpdateSchema,
781
797
  returnCreateSchema,
798
+ returnDeleteSchema,
799
+ returnUpdateSchema,
782
800
  salesEditingSettingsSchema,
783
801
  salesSettingsUpsertSchema,
784
802
  salesTagCreateSchema,