@open-mercato/core 0.10.1-develop.7833.1.d6cbf0e3ab → 0.10.1-develop.7836.1.0ef0f453c6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/cart/api/current/lines/bulk/route.js +1 -1
- package/dist/modules/cart/api/current/lines/bulk/route.js.map +2 -2
- package/dist/modules/cart/api/current/lines/route.js +1 -1
- package/dist/modules/cart/api/current/lines/route.js.map +2 -2
- package/dist/modules/cart/commands/lines.js +5 -10
- package/dist/modules/cart/commands/lines.js.map +2 -2
- package/dist/modules/cart/lib/currentCartRoute.js +8 -0
- package/dist/modules/cart/lib/currentCartRoute.js.map +3 -3
- package/dist/modules/cart/lib/lineMutationLock.js +52 -0
- package/dist/modules/cart/lib/lineMutationLock.js.map +7 -0
- package/dist/modules/sales/commands/documents.js +3 -3
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +75 -21
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/cart/api/current/lines/bulk/route.ts +1 -1
- package/src/modules/cart/api/current/lines/route.ts +1 -1
- package/src/modules/cart/commands/lines.ts +5 -10
- package/src/modules/cart/i18n/de.json +1 -0
- package/src/modules/cart/i18n/en.json +1 -0
- package/src/modules/cart/i18n/es.json +1 -0
- package/src/modules/cart/i18n/ko.json +1 -0
- package/src/modules/cart/i18n/pl.json +1 -0
- package/src/modules/cart/lib/currentCartRoute.ts +8 -0
- package/src/modules/cart/lib/lineMutationLock.ts +59 -0
- package/src/modules/sales/commands/documents.ts +3 -3
- package/src/modules/sales/components/documents/ItemsSection.tsx +82 -23
- package/src/modules/sales/components/documents/lineItemUtils.ts +2 -2
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/ko.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { extensionPoints } from "@open-mercato/core/modules/sales/extension-points";
|
|
5
5
|
import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
@@ -7,7 +7,8 @@ import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimi
|
|
|
7
7
|
import { deleteCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
8
8
|
import { normalizeCrudServerError } from "@open-mercato/ui/backend/utils/serverErrors";
|
|
9
9
|
import { ErrorMessage, LoadingMessage, TabEmptyState } from "@open-mercato/ui/backend/detail";
|
|
10
|
-
import {
|
|
10
|
+
import { IconButton } from "@open-mercato/ui/primitives/icon-button";
|
|
11
|
+
import { SimpleTooltip } from "@open-mercato/ui/primitives/tooltip";
|
|
11
12
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
12
13
|
import { Pencil, Trash2 } from "lucide-react";
|
|
13
14
|
import {
|
|
@@ -34,6 +35,50 @@ import { useInjectionDataWidgets } from "@open-mercato/ui/backend/injection/useI
|
|
|
34
35
|
import { OrderItemsInjectionContext } from "../../widgets/injection/order-items-context.js";
|
|
35
36
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
36
37
|
const logger = createLogger("sales");
|
|
38
|
+
function DisabledActionHint({
|
|
39
|
+
label,
|
|
40
|
+
reason,
|
|
41
|
+
children
|
|
42
|
+
}) {
|
|
43
|
+
const [open, setOpen] = React.useState(false);
|
|
44
|
+
const touchOpenRef = React.useRef(false);
|
|
45
|
+
if (!reason) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
SimpleTooltip,
|
|
48
|
+
{
|
|
49
|
+
content: reason,
|
|
50
|
+
open,
|
|
51
|
+
onOpenChange: (nextOpen) => {
|
|
52
|
+
if (nextOpen || !touchOpenRef.current) setOpen(nextOpen);
|
|
53
|
+
},
|
|
54
|
+
children: /* @__PURE__ */ jsx(
|
|
55
|
+
"span",
|
|
56
|
+
{
|
|
57
|
+
className: "inline-flex rounded-md focus-visible:outline-none focus-visible:shadow-focus",
|
|
58
|
+
tabIndex: 0,
|
|
59
|
+
"aria-label": `${label}: ${reason}`,
|
|
60
|
+
onClick: (event) => event.stopPropagation(),
|
|
61
|
+
onPointerDown: (event) => {
|
|
62
|
+
if (event.pointerType !== "touch") return;
|
|
63
|
+
touchOpenRef.current = true;
|
|
64
|
+
setOpen(true);
|
|
65
|
+
event.currentTarget.focus();
|
|
66
|
+
},
|
|
67
|
+
onBlur: () => {
|
|
68
|
+
touchOpenRef.current = false;
|
|
69
|
+
setOpen(false);
|
|
70
|
+
},
|
|
71
|
+
onKeyDown: (event) => {
|
|
72
|
+
if (event.key !== "Escape") return;
|
|
73
|
+
touchOpenRef.current = false;
|
|
74
|
+
setOpen(false);
|
|
75
|
+
},
|
|
76
|
+
children
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
37
82
|
function isPlainObject(value) {
|
|
38
83
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39
84
|
}
|
|
@@ -635,11 +680,11 @@ function SalesDocumentItemsSection({
|
|
|
635
680
|
] }) }),
|
|
636
681
|
showDiscountColumn ? /* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: discount ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
637
682
|
discount.amount !== null ? /* @__PURE__ */ jsx("span", { className: "font-mono text-sm", children: t(
|
|
638
|
-
"sales.documents.items.table.discountAmount",
|
|
639
|
-
"\u2212{{value}}",
|
|
683
|
+
discount.amount < 0 ? "sales.documents.items.table.markupAmount" : "sales.documents.items.table.discountAmount",
|
|
684
|
+
discount.amount < 0 ? "+{{value}}" : "\u2212{{value}}",
|
|
640
685
|
{
|
|
641
686
|
value: formatMoney(
|
|
642
|
-
discount.amount,
|
|
687
|
+
Math.abs(discount.amount),
|
|
643
688
|
item.currencyCode ?? currencyCode ?? void 0,
|
|
644
689
|
locale
|
|
645
690
|
)
|
|
@@ -695,30 +740,39 @@ function SalesDocumentItemsSection({
|
|
|
695
740
|
}),
|
|
696
741
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 justify-end", children: [
|
|
697
742
|
/* @__PURE__ */ jsx(
|
|
698
|
-
|
|
743
|
+
DisabledActionHint,
|
|
699
744
|
{
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
745
|
+
label: t("ui.actions.edit", "Edit"),
|
|
746
|
+
reason: amountsReadOnly ? t("sales.documents.amountsExternalHint", "Totals on this document come from an external system and are stored as supplied.") : void 0,
|
|
747
|
+
children: /* @__PURE__ */ jsx(
|
|
748
|
+
IconButton,
|
|
749
|
+
{
|
|
750
|
+
type: "button",
|
|
751
|
+
size: "default",
|
|
752
|
+
variant: "ghost",
|
|
753
|
+
"aria-label": t("ui.actions.edit", "Edit"),
|
|
754
|
+
disabled: amountsReadOnly,
|
|
755
|
+
onClick: (event) => {
|
|
756
|
+
event.stopPropagation();
|
|
757
|
+
handleEdit(item);
|
|
758
|
+
},
|
|
759
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
|
|
760
|
+
}
|
|
761
|
+
)
|
|
710
762
|
}
|
|
711
763
|
),
|
|
712
764
|
/* @__PURE__ */ jsx(
|
|
713
|
-
|
|
765
|
+
DisabledActionHint,
|
|
714
766
|
{
|
|
715
|
-
|
|
767
|
+
label: t("ui.actions.delete", "Delete"),
|
|
768
|
+
reason: amountsReadOnly ? t("sales.documents.amountsExternalHint", "Totals on this document come from an external system and are stored as supplied.") : kind === "order" && items.length === 1 ? t("sales.documents.items.errorDeleteLast", "An order must contain at least one line item.") : void 0,
|
|
716
769
|
children: /* @__PURE__ */ jsx(
|
|
717
|
-
|
|
770
|
+
IconButton,
|
|
718
771
|
{
|
|
719
|
-
|
|
772
|
+
type: "button",
|
|
773
|
+
size: "default",
|
|
720
774
|
variant: "ghost",
|
|
721
|
-
className: "
|
|
775
|
+
className: "text-destructive",
|
|
722
776
|
"aria-label": t("ui.actions.delete", "Delete"),
|
|
723
777
|
disabled: amountsReadOnly || kind === "order" && items.length === 1,
|
|
724
778
|
onClick: (event) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sales/components/documents/ItemsSection.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport * as React from \"react\";\nimport { extensionPoints } from \"@open-mercato/core/modules/sales/extension-points\";\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 { normalizeCrudServerError } from \"@open-mercato/ui/backend/utils/serverErrors\";\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from \"@open-mercato/ui/backend/detail\";\nimport { Button } from \"@open-mercato/ui/primitives/button\";\nimport { flash } from \"@open-mercato/ui/backend/FlashMessages\";\nimport { Pencil, Trash2 } from \"lucide-react\";\nimport {\n DictionaryValue,\n type DictionaryMap,\n createDictionaryMap,\n normalizeDictionaryEntries,\n} from \"@open-mercato/core/modules/dictionaries/components/dictionaryAppearance\";\nimport { cn } from \"@open-mercato/shared/lib/utils\";\nimport { useT, useLocale } from \"@open-mercato/shared/lib/i18n/context\";\nimport { useOrganizationScopeDetail } from \"@open-mercato/shared/lib/frontend/useOrganizationScope\";\nimport { useConfirmDialog } from \"@open-mercato/ui/backend/confirm-dialog\";\nimport { emitSalesDocumentTotalsRefresh } from \"@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents\";\nimport { LineItemDialog } from \"./LineItemDialog\";\nimport { handleSectionMutationError } from \"./optimisticLock\";\nimport { useSalesDocumentMutation } from \"./useSalesDocumentMutation\";\nimport type { SalesLineRecord } from \"./lineItemTypes\";\nimport {\n formatMoney,\n normalizeNumber,\n resolveLineDiscountDisplay,\n} from \"./lineItemUtils\";\nimport type { SectionAction } from \"@open-mercato/ui/backend/detail\";\nimport { extractCustomFieldValues } from \"./customFieldHelpers\";\nimport { canonicalizeUnitCode } from \"@open-mercato/shared/lib/units/unitCodes\";\nimport type { SalesLineUomSnapshot } from \"../../lib/types\";\nimport { useInjectionDataWidgets } from \"@open-mercato/ui/backend/injection/useInjectionDataWidgets\";\nimport type { InjectionColumnDefinition } from \"@open-mercato/shared/modules/widgets/injection\";\nimport { OrderItemsInjectionContext } from \"../../widgets/injection/order-items-context\";\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype ResolvedUnitPriceReference = {\n grossPerReference: number;\n referenceUnitCode: string;\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isSalesLineUomSnapshot(\n value: unknown,\n): value is SalesLineUomSnapshot {\n if (!isPlainObject(value)) return false;\n return (\n value.version === 1 &&\n typeof value.enteredQuantity === \"string\" &&\n typeof value.toBaseFactor === \"string\" &&\n typeof value.normalizedQuantity === \"string\"\n );\n}\n\nfunction extractUomSnapshot(\n item: Record<string, unknown>,\n): SalesLineUomSnapshot | null {\n const raw = item.uom_snapshot ?? item.uomSnapshot;\n if (isSalesLineUomSnapshot(raw)) return raw;\n return null;\n}\n\nfunction resolveUnitPriceReference(\n snapshot: SalesLineUomSnapshot | Record<string, unknown> | null,\n): ResolvedUnitPriceReference | null {\n if (!snapshot) return null;\n\n const ref = isSalesLineUomSnapshot(snapshot)\n ? snapshot.unitPriceReference\n : isPlainObject(snapshot)\n ? isPlainObject(snapshot.unitPriceReference)\n ? snapshot.unitPriceReference\n : isPlainObject(snapshot.unit_price_reference)\n ? snapshot.unit_price_reference\n : null\n : null;\n\n if (!ref || !isPlainObject(ref)) return null;\n\n const refRecord = ref as Record<string, unknown>;\n const grossPerReference = normalizeNumber(\n refRecord.grossPerReference ?? refRecord.gross_per_reference,\n Number.NaN,\n );\n if (!Number.isFinite(grossPerReference)) return null;\n\n const referenceUnitCode =\n typeof refRecord.referenceUnitCode === \"string\"\n ? refRecord.referenceUnitCode\n : typeof refRecord.reference_unit_code === \"string\"\n ? refRecord.reference_unit_code\n : typeof refRecord.referenceUnit === \"string\"\n ? refRecord.referenceUnit\n : null;\n if (!referenceUnitCode) return null;\n\n return { grossPerReference, referenceUnitCode };\n}\n\nfunction getUomFields(item: Record<string, unknown>) {\n const uomSnapshot = extractUomSnapshot(item);\n return {\n normalizedQuantity: (item.normalized_quantity ?? item.normalizedQuantity ?? null) as\n | number\n | string\n | null,\n normalizedUnit: (item.normalized_unit ?? item.normalizedUnit ?? null) as string | null,\n quantityUnit: (item.quantity_unit ?? item.quantityUnit ?? null) as string | null,\n uomSnapshot,\n };\n}\n\nfunction resolveInjectedColumnValue(\n row: Record<string, unknown>,\n accessorKey: string,\n): unknown {\n const segments = accessorKey.split(\".\").filter(Boolean);\n let current: unknown = row;\n for (const segment of segments) {\n if (!current || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\nconst LINES_PAGE_SIZE = 100;\n// Upper bound on line pages fetched for one document, so an API that ignores `page`\n// cannot spin the loader forever.\nconst LINES_MAX_PAGES = 200;\n\nconst SHIPMENTS_PAGE_SIZE = 100;\n// A single order beyond this many shipment pages is pathological; stopping there\n// keeps the shipped state explicitly unresolved rather than silently partial.\nconst SHIPMENTS_MAX_PAGES = 50;\n\ntype SalesDocumentItemsSectionProps = {\n documentId: string;\n kind: \"order\" | \"quote\";\n currencyCode: string | null | undefined;\n documentUpdatedAt?: string | null;\n organizationId?: string | null;\n tenantId?: string | null;\n onActionChange?: (action: SectionAction | null) => void;\n onItemsChange?: (items: SalesLineRecord[]) => void;\n /** Disables line writes: on an external order they would all be rejected. */\n amountsReadOnly?: boolean;\n};\n\nexport function SalesDocumentItemsSection({\n documentId,\n kind,\n currencyCode,\n documentUpdatedAt,\n organizationId: orgFromProps,\n tenantId: tenantFromProps,\n onActionChange,\n onItemsChange,\n amountsReadOnly = false,\n}: SalesDocumentItemsSectionProps) {\n const t = useT();\n const runDocumentMutation = useSalesDocumentMutation(kind, documentId);\n const locale = useLocale();\n const { organizationId, tenantId } = useOrganizationScopeDetail();\n const { confirm, ConfirmDialogElement } = useConfirmDialog();\n const resolvedOrganizationId = orgFromProps ?? organizationId ?? null;\n const resolvedTenantId = tenantFromProps ?? tenantId ?? null;\n const [items, setItems] = React.useState<SalesLineRecord[]>([]);\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 [lineForEdit, setLineForEdit] = React.useState<SalesLineRecord | null>(\n null,\n );\n const [lineStatusMap, setLineStatusMap] = React.useState<DictionaryMap>({});\n const [shippedTotals, setShippedTotals] = React.useState<Map<string, number>>(\n new Map(),\n );\n // Quotes have no shipments at all, so their shipped state is known up front.\n // For orders it stays unknown until every shipment page has been read \u2014 an\n // empty map from a pending or failed load must never read as \"nothing shipped\".\n const [shippedTotalsResolved, setShippedTotalsResolved] = React.useState(\n kind !== \"order\",\n );\n\n const { widgets: allColumnWidgets, isLoading: injectedColumnsLoading, error: injectedColumnsError } = useInjectionDataWidgets(\n extensionPoints.hosts.orderItemColumns.spotId,\n );\n const columnWidgets = kind === \"order\" ? allColumnWidgets : [];\n const injectedColumns = React.useMemo<InjectionColumnDefinition[]>(() => {\n const cols: InjectionColumnDefinition[] = [];\n for (const widget of columnWidgets) {\n if (!(\"columns\" in widget)) continue;\n for (const def of (widget as { columns?: InjectionColumnDefinition[] })\n .columns ?? []) {\n cols.push(def);\n }\n }\n return cols;\n }, [columnWidgets]);\n\n const resourcePath = React.useMemo(\n () => (kind === \"order\" ? \"sales/order-lines\" : \"sales/quote-lines\"),\n [kind],\n );\n const documentKey = kind === \"order\" ? \"orderId\" : \"quoteId\";\n const lineStatusesLoaded = React.useRef(false);\n const itemsLoadedForDocument = React.useRef<string | null>(null);\n const itemsLoadSeq = React.useRef(0);\n const shipmentsLoadedForDocument = React.useRef<string | null>(null);\n const loadLineStatuses = React.useCallback(async () => {\n try {\n const params = new URLSearchParams({ page: \"1\", pageSize: \"100\" });\n const response = await apiCall<{\n items?: Array<Record<string, unknown>>;\n }>(`/api/sales/order-line-statuses?${params.toString()}`, undefined, {\n fallback: { items: [] },\n });\n const entries = normalizeDictionaryEntries(response.result?.items ?? [], { sort: false });\n setLineStatusMap(createDictionaryMap(entries));\n } catch (err) {\n logger.error('sales.document.line-statuses.load', { err });\n setLineStatusMap({});\n }\n }, []);\n\n const loadItems = React.useCallback(async () => {\n const loadSeq = ++itemsLoadSeq.current;\n const isCurrentLoad = () => loadSeq === itemsLoadSeq.current;\n setLoading(true);\n setError(null);\n try {\n const collectedItems: Array<Record<string, unknown>> = [];\n const seenIds = new Set<string>();\n let loaded = false;\n let complete = false;\n let page = 1;\n while (page <= LINES_MAX_PAGES) {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(LINES_PAGE_SIZE),\n [documentKey]: documentId,\n });\n const response = await apiCall<{\n items?: Array<Record<string, unknown>>;\n total?: unknown;\n }>(`/api/${resourcePath}?${params.toString()}`, undefined, {\n fallback: { items: [] },\n });\n if (!response.ok || !Array.isArray(response.result?.items)) {\n if (page > 1) throw new Error(\"[internal] sales line page failed to load\");\n complete = true;\n break;\n }\n loaded = true;\n const pageItems = response.result.items;\n let added = 0;\n pageItems.forEach((item) => {\n const id = typeof item.id === \"string\" ? item.id : null;\n if (id && seenIds.has(id)) return;\n if (id) seenIds.add(id);\n collectedItems.push(item);\n added += 1;\n });\n const reportedTotal = normalizeNumber(response.result?.total, Number.NaN);\n const hasMore =\n added > 0 &&\n pageItems.length >= LINES_PAGE_SIZE &&\n (!Number.isFinite(reportedTotal) || collectedItems.length < reportedTotal);\n if (!hasMore) {\n complete = true;\n break;\n }\n page += 1;\n }\n if (!complete) throw new Error(\"[internal] sales line page limit reached\");\n if (!isCurrentLoad()) return;\n if (loaded) {\n const mapped = collectedItems.flatMap<SalesLineRecord>(\n (item) => {\n const id = typeof item.id === \"string\" ? item.id : null;\n if (!id) return [];\n const taxRate = normalizeNumber(\n item.tax_rate ?? item.taxRate,\n 0,\n );\n const customFields = extractCustomFieldValues(\n item as Record<string, unknown>,\n );\n const name =\n typeof item.name === \"string\"\n ? item.name\n : typeof item.catalog_snapshot === \"object\" &&\n item.catalog_snapshot &&\n typeof (item.catalog_snapshot as Record<string, unknown>).name === \"string\"\n ? (item.catalog_snapshot as Record<string, unknown>).name as string\n : null;\n const description =\n typeof item.description === \"string\" && item.description.trim()\n ? item.description\n : null;\n const quantity = normalizeNumber(item.quantity, 0);\n const uomFields = getUomFields(item);\n const quantityUnit = canonicalizeUnitCode(uomFields.quantityUnit);\n const normalizedQuantity = normalizeNumber(\n uomFields.normalizedQuantity,\n quantity,\n );\n const normalizedUnit =\n canonicalizeUnitCode(uomFields.normalizedUnit) ?? quantityUnit;\n const uomSnapshot = uomFields.uomSnapshot;\n const unitPriceNetRaw = normalizeNumber(\n item.unit_price_net ?? item.unitPriceNet,\n Number.NaN,\n );\n const unitPriceGrossRaw = normalizeNumber(\n item.unit_price_gross ?? item.unitPriceGross,\n Number.NaN,\n );\n const unitPriceNet = Number.isFinite(unitPriceNetRaw)\n ? unitPriceNetRaw\n : Number.isFinite(unitPriceGrossRaw)\n ? unitPriceGrossRaw / (1 + taxRate / 100)\n : 0;\n const unitPriceGross = Number.isFinite(unitPriceGrossRaw)\n ? unitPriceGrossRaw\n : Number.isFinite(unitPriceNetRaw)\n ? unitPriceNetRaw * (1 + taxRate / 100)\n : 0;\n const totalNetRaw = normalizeNumber(\n item.total_net_amount ?? item.totalNetAmount,\n Number.NaN,\n );\n const totalGrossRaw = normalizeNumber(\n item.total_gross_amount ?? item.totalGrossAmount,\n Number.NaN,\n );\n const totalNet = Number.isFinite(totalNetRaw)\n ? totalNetRaw\n : unitPriceNet * quantity;\n const totalGross = Number.isFinite(totalGrossRaw)\n ? totalGrossRaw\n : unitPriceGross * quantity;\n const priceModeRaw =\n item.metadata &&\n typeof item.metadata === \"object\"\n ? (item.metadata as Record<string, unknown>).priceMode\n : null;\n const priceMode = priceModeRaw === \"net\" ? \"net\" : \"gross\";\n const customFieldSetId =\n typeof item.custom_field_set_id === \"string\"\n ? item.custom_field_set_id\n : typeof item.customFieldSetId === \"string\"\n ? item.customFieldSetId\n : null;\n const statusEntryId =\n typeof item.status_entry_id === \"string\"\n ? item.status_entry_id\n : typeof item.statusEntryId === \"string\"\n ? item.statusEntryId\n : null;\n const status = typeof item.status === \"string\" ? item.status : null;\n const record: SalesLineRecord = {\n id,\n name,\n description,\n productId:\n typeof item.product_id === \"string\" ? item.product_id : null,\n productVariantId:\n typeof item.product_variant_id === \"string\"\n ? item.product_variant_id\n : null,\n quantity,\n quantityUnit,\n normalizedQuantity,\n normalizedUnit,\n currencyCode:\n typeof item.currency_code === \"string\"\n ? item.currency_code\n : typeof currencyCode === \"string\"\n ? currencyCode\n : null,\n unitPriceNet,\n unitPriceGross,\n discountAmount: normalizeNumber(\n item.discount_amount ?? item.discountAmount,\n 0,\n ),\n discountPercent: normalizeNumber(\n item.discount_percent ?? item.discountPercent,\n 0,\n ),\n taxRate,\n totalNet,\n totalGross,\n priceMode,\n uomSnapshot,\n metadata:\n (item.metadata as Record<string, unknown> | null | undefined) ??\n null,\n catalogSnapshot:\n (item.catalog_snapshot as\n | Record<string, unknown>\n | null\n | undefined) ?? null,\n customFieldSetId,\n customFields: Object.keys(customFields).length\n ? customFields\n : null,\n status,\n statusEntryId,\n };\n return [record];\n },\n );\n setItems(mapped);\n if (onItemsChange) onItemsChange(mapped);\n } else {\n setItems([]);\n if (onItemsChange) onItemsChange([]);\n }\n } catch (err) {\n if (!isCurrentLoad()) return;\n logger.error('sales.document.items.load', { err });\n setError(t(\"sales.documents.items.errorLoad\", \"Failed to load items.\"));\n setItems([]);\n if (onItemsChange) onItemsChange([]);\n } finally {\n if (isCurrentLoad()) setLoading(false);\n }\n }, [currencyCode, documentId, documentKey, onItemsChange, resourcePath, t]);\n\n const loadShippedTotals = React.useCallback(async () => {\n if (kind !== \"order\") {\n setShippedTotals(new Map());\n setShippedTotalsResolved(true);\n return;\n }\n setShippedTotals(new Map());\n setShippedTotalsResolved(false);\n try {\n const totals = new Map<string, number>();\n let page = 1;\n let collected = 0;\n let complete = false;\n while (page <= SHIPMENTS_MAX_PAGES) {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(SHIPMENTS_PAGE_SIZE),\n orderId: documentId,\n });\n const response = await apiCall<{\n items?: Array<Record<string, unknown>>;\n total?: unknown;\n }>(`/api/sales/shipments?${params.toString()}`, undefined, {\n fallback: { items: [] },\n });\n if (!response.ok || !Array.isArray(response.result?.items)) return;\n const shipments = response.result.items;\n shipments.forEach((shipment) => {\n const entries = Array.isArray(shipment.items)\n ? (shipment.items as Array<Record<string, unknown>>)\n : [];\n entries.forEach((entry) => {\n const lineId =\n typeof entry.orderLineId === \"string\"\n ? entry.orderLineId\n : typeof entry.order_line_id === \"string\"\n ? entry.order_line_id\n : null;\n if (!lineId) return;\n const quantity = normalizeNumber(entry.quantity, 0);\n if (!Number.isFinite(quantity) || quantity <= 0) return;\n const current = totals.get(lineId) ?? 0;\n totals.set(lineId, current + quantity);\n });\n });\n collected += shipments.length;\n const reportedTotal = normalizeNumber(response.result?.total, Number.NaN);\n const hasMore =\n shipments.length >= SHIPMENTS_PAGE_SIZE &&\n (!Number.isFinite(reportedTotal) || collected < reportedTotal);\n if (!hasMore) {\n complete = true;\n break;\n }\n page += 1;\n }\n if (!complete) return;\n setShippedTotals(totals);\n setShippedTotalsResolved(true);\n } catch (err) {\n logger.error('sales.document.shipments.load', { err });\n }\n }, [documentId, kind]);\n\n React.useEffect(() => {\n if (lineStatusesLoaded.current) return;\n lineStatusesLoaded.current = true;\n void loadLineStatuses();\n }, [loadLineStatuses]);\n\n React.useEffect(() => {\n if (!documentId) return;\n if (itemsLoadedForDocument.current === documentId) return;\n itemsLoadedForDocument.current = documentId;\n void loadItems();\n }, [documentId, loadItems]);\n\n React.useEffect(() => {\n if (kind !== \"order\") {\n shipmentsLoadedForDocument.current = null;\n setShippedTotals(new Map());\n setShippedTotalsResolved(true);\n return;\n }\n const key = `${kind}:${documentId}`;\n if (shipmentsLoadedForDocument.current === key) return;\n shipmentsLoadedForDocument.current = key;\n void loadShippedTotals();\n }, [documentId, kind, loadShippedTotals]);\n\n const openCreate = React.useCallback(() => {\n setLineForEdit(null);\n setDialogOpen(true);\n }, []);\n\n React.useEffect(() => {\n if (!onActionChange) return;\n onActionChange({\n label: t(\"sales.documents.items.add\", \"Add item\"),\n onClick: openCreate,\n disabled: amountsReadOnly,\n });\n return () => onActionChange(null);\n }, [amountsReadOnly, onActionChange, openCreate, t]);\n\n const handleEdit = React.useCallback(\n (line: SalesLineRecord) => {\n if (amountsReadOnly) return;\n setLineForEdit(line);\n setDialogOpen(true);\n },\n [amountsReadOnly],\n );\n\n const resolveVariantInfo = React.useCallback((record: SalesLineRecord) => {\n const meta =\n (record.metadata as Record<string, unknown> | null | undefined) ?? null;\n const snapshot =\n (record.catalogSnapshot as Record<string, unknown> | null | undefined) ??\n null;\n const variantSnapshot =\n snapshot &&\n typeof snapshot.variant === \"object\" &&\n snapshot.variant\n ? (snapshot.variant as Record<string, unknown>)\n : null;\n const variantTitle =\n meta && typeof meta.variantTitle === \"string\"\n ? meta.variantTitle\n : variantSnapshot && typeof variantSnapshot.name === \"string\"\n ? variantSnapshot.name\n : null;\n const variantSku =\n meta && typeof meta.variantSku === \"string\"\n ? meta.variantSku\n : variantSnapshot && typeof variantSnapshot.sku === \"string\"\n ? variantSnapshot.sku\n : null;\n\n return { variantTitle, variantSku };\n }, []);\n\n const handleDelete = React.useCallback(\n async (line: SalesLineRecord) => {\n const confirmed = await confirm({\n title: t(\n \"sales.documents.items.deleteConfirm\",\n \"Delete this line item?\",\n ),\n variant: \"destructive\",\n });\n if (!confirmed) return;\n try {\n const result = await runDocumentMutation(() => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(documentUpdatedAt),\n () =>\n deleteCrud(resourcePath, {\n body: {\n id: line.id,\n [documentKey]: documentId,\n organizationId: resolvedOrganizationId ?? undefined,\n tenantId: resolvedTenantId ?? undefined,\n },\n errorMessage: t(\n \"sales.documents.items.errorDelete\",\n \"Failed to delete line.\",\n ),\n }),\n ), { id: line.id, [documentKey]: documentId });\n if (result.ok) {\n flash(t(\"sales.documents.items.deleted\", \"Line removed.\"), \"success\");\n await loadItems();\n emitSalesDocumentTotalsRefresh({ documentId, kind });\n }\n } catch (err) {\n if (handleSectionMutationError(err, t, () => void loadItems())) return;\n logger.error('sales.document.items.delete', { err });\n const normalized = normalizeCrudServerError(err);\n const fallback = t(\n \"sales.documents.items.errorDelete\",\n \"Failed to delete line.\",\n );\n flash(normalized.message || fallback, \"error\");\n }\n },\n [\n confirm,\n documentId,\n documentKey,\n documentUpdatedAt,\n kind,\n loadItems,\n resolvedOrganizationId,\n resourcePath,\n t,\n resolvedTenantId,\n runDocumentMutation,\n ],\n );\n\n const renderStatus = React.useCallback(\n (line: SalesLineRecord) => {\n const value = line.status ?? null;\n if (!value) {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"sales.documents.items.table.statusEmpty\", \"No status\")}\n </span>\n );\n }\n return (\n <DictionaryValue\n value={value}\n map={lineStatusMap}\n fallback={<span className=\"text-xs font-medium\">{value}</span>}\n className=\"text-xs font-medium\"\n iconWrapperClassName=\"inline-flex h-6 w-6 items-center justify-center rounded bg-muted text-muted-foreground\"\n iconClassName=\"h-3.5 w-3.5\"\n colorClassName=\"h-4 w-4 rounded-full border border-border/70\"\n />\n );\n },\n [lineStatusMap, t],\n );\n\n const showDiscountColumn = React.useMemo(\n () => items.some((item) => resolveLineDiscountDisplay(item) !== null),\n [items],\n );\n\n const renderImage = (record: SalesLineRecord) => {\n const meta =\n (record.metadata as Record<string, unknown> | null | undefined) ?? {};\n const snapshot =\n (record.catalogSnapshot as Record<string, unknown> | null | undefined) ??\n {};\n const productSnapshot: Record<string, unknown> =\n typeof snapshot === \"object\" && snapshot\n ? ((snapshot.product as Record<string, unknown>) ?? {})\n : {};\n const variantSnapshot: Record<string, unknown> =\n typeof snapshot === \"object\" && snapshot\n ? ((snapshot.variant as Record<string, unknown>) ?? {})\n : {};\n const productThumb =\n (meta &&\n typeof meta.productThumbnail === \"string\" &&\n meta.productThumbnail) ||\n (productSnapshot &&\n typeof productSnapshot.thumbnailUrl === \"string\" &&\n productSnapshot.thumbnailUrl) ||\n null;\n const variantThumb =\n (meta &&\n typeof meta.variantThumbnail === \"string\" &&\n meta.variantThumbnail) ||\n (variantSnapshot &&\n typeof variantSnapshot.thumbnailUrl === \"string\" &&\n variantSnapshot.thumbnailUrl) ||\n null;\n const thumbnail = variantThumb ?? productThumb;\n if (thumbnail) {\n return (\n <img\n src={thumbnail}\n alt={record.name ?? record.id}\n className=\"h-10 w-10 rounded border object-cover\"\n />\n );\n }\n return (\n <div className=\"flex h-10 w-10 items-center justify-center rounded border bg-muted text-xs text-muted-foreground\">\n N/A\n </div>\n );\n };\n\n return (\n <OrderItemsInjectionContext.Provider value={{ documentId, kind }}>\n <div className=\"space-y-4\">\n {kind === \"order\" && injectedColumnsError ? (\n <ErrorMessage label={t('ui.dataTable.extensions.error', 'Some table extensions could not load. Reload the page to retry.')} />\n ) : null}\n {loading || (kind === \"order\" && injectedColumnsLoading) ? (\n <LoadingMessage\n label={t(\"sales.documents.items.loading\", \"Loading items\u2026\")}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : error ? (\n <p className=\"text-sm text-destructive\">{error}</p>\n ) : items.length === 0 ? (\n <TabEmptyState\n title={t(\"sales.documents.items.empty\", \"No items yet.\")}\n description={t(\n \"sales.documents.items.subtitle\",\n \"Add products and configure pricing for this document.\",\n )}\n action={{\n label: t(\"sales.documents.items.add\", \"Add item\"),\n onClick: openCreate,\n disabled: amountsReadOnly,\n }}\n />\n ) : (\n <div className=\"relative overflow-x-auto rounded border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted\">\n <tr className=\"text-left\">\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.product\", \"Product\")}\n </th>\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.status\", \"Status\")}\n </th>\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.quantity\", \"Qty\")}\n </th>\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.unit\", \"Unit price\")}\n </th>\n {showDiscountColumn ? (\n <th className=\"px-3 py-2 font-medium whitespace-nowrap\">\n {t(\"sales.documents.items.table.discount\", \"Discount\")}\n </th>\n ) : null}\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.total\", \"Total\")}\n </th>\n {injectedColumns.map((col) => (\n <th\n key={col.id}\n className=\"px-3 py-2 font-medium whitespace-nowrap\"\n >\n {col.headerKey ? t(col.headerKey, col.header) : col.header}\n </th>\n ))}\n <th className=\"px-3 py-2 font-medium sr-only\">\n {t(\"sales.documents.items.table.actions\", \"Actions\")}\n </th>\n </tr>\n </thead>\n <tbody>\n {items.map((item) => {\n const meta =\n (item.metadata as\n | Record<string, unknown>\n | null\n | undefined) ?? null;\n const { variantTitle, variantSku } = resolveVariantInfo(item);\n const productSku =\n meta && typeof meta.productSku === \"string\"\n ? meta.productSku\n : null;\n const variantLabel = variantTitle ?? variantSku;\n const variantSuffix =\n variantSku && variantLabel && variantSku !== variantLabel\n ? ` \u2022 ${variantSku}`\n : \"\";\n const showProductSku =\n productSku && productSku !== variantSku ? productSku : null;\n const shippedQuantity = Math.max(\n 0,\n shippedTotals.get(item.id) ?? 0,\n );\n const quantityLabel = item.quantityUnit\n ? `${item.quantity} ${item.quantityUnit}`\n : String(item.quantity);\n const showNormalized =\n Number.isFinite(item.normalizedQuantity) &&\n item.normalizedQuantity > 0 &&\n (item.normalizedUnit ?? null) &&\n (Math.abs(item.normalizedQuantity - item.quantity) >\n 0.000001 ||\n (item.normalizedUnit ?? null) !==\n (item.quantityUnit ?? null));\n const unitPriceReference = resolveUnitPriceReference(\n item.uomSnapshot,\n );\n const discount = resolveLineDiscountDisplay(item);\n\n return (\n <tr\n key={item.id}\n className={cn(\n \"border-t transition-colors\",\n amountsReadOnly ? \"\" : \"cursor-pointer hover:bg-muted/50\",\n )}\n onClick={() => handleEdit(item)}\n >\n <td className=\"px-3 py-3\">\n <div className=\"flex items-center gap-3\">\n {renderImage(item)}\n <div className=\"min-w-0\">\n <div className=\"truncate font-medium\">\n {item.name ??\n t(\"sales.documents.items.untitled\", \"Untitled\")}\n </div>\n {variantLabel ? (\n <div className=\"text-xs text-muted-foreground truncate\">\n {variantLabel}\n {variantSuffix}\n </div>\n ) : null}\n {showProductSku ? (\n <div className=\"text-xs text-muted-foreground truncate\">\n {showProductSku}\n </div>\n ) : null}\n {item.description ? (\n <div\n className=\"text-xs text-muted-foreground line-clamp-2\"\n title={item.description}\n >\n {item.description}\n </div>\n ) : null}\n </div>\n </div>\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex items-center\">\n {renderStatus(item)}\n </div>\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex flex-col gap-0.5\">\n <span className=\"font-medium\">{quantityLabel}</span>\n {showNormalized ? (\n <span className=\"text-xs text-muted-foreground\">\n {item.normalizedQuantity} {item.normalizedUnit}\n </span>\n ) : null}\n {shippedQuantity > 0 ? (\n <span className=\"text-xs text-muted-foreground\">\n {t(\n \"sales.documents.items.table.shipped\",\n \"{{shipped}} / {{total}} shipped\",\n {\n shipped: shippedQuantity,\n total: item.quantity,\n },\n )}\n </span>\n ) : null}\n </div>\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex flex-col gap-0.5\">\n <span className=\"font-mono text-sm\">\n {formatMoney(\n item.unitPriceGross,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"sales.documents.items.table.gross\", \"gross\")}\n </span>\n </span>\n <span className=\"font-mono text-xs text-muted-foreground\">\n {formatMoney(\n item.unitPriceNet,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n {t(\"sales.documents.items.table.net\", \"net\")}\n </span>\n {unitPriceReference ? (\n <span className=\"text-xs text-muted-foreground\">\n {t(\n \"sales.documents.items.table.unitPriceReference\",\n \"{{value}} per 1 {{unit}}\",\n {\n value: formatMoney(\n unitPriceReference.grossPerReference,\n item.currencyCode ??\n currencyCode ??\n undefined,\n locale,\n ),\n unit: unitPriceReference.referenceUnitCode,\n },\n )}\n </span>\n ) : null}\n </div>\n </td>\n {showDiscountColumn ? (\n <td className=\"px-3 py-3\">\n {discount ? (\n <div className=\"flex flex-col gap-0.5\">\n {discount.amount !== null ? (\n <span className=\"font-mono text-sm\">\n {t(\n \"sales.documents.items.table.discountAmount\",\n \"\u2212{{value}}\",\n {\n value: formatMoney(\n discount.amount,\n item.currencyCode ??\n currencyCode ??\n undefined,\n locale,\n ),\n },\n )}\n </span>\n ) : null}\n {discount.percent !== null ? (\n <span\n className={\n discount.amount !== null\n ? \"font-mono text-xs text-muted-foreground\"\n : \"font-mono text-sm\"\n }\n >\n {t(\n \"sales.documents.items.table.discountPercent\",\n \"{{value}}%\",\n { value: discount.percent },\n )}\n </span>\n ) : null}\n </div>\n ) : null}\n </td>\n ) : null}\n <td className=\"px-3 py-3 font-semibold\">\n <div className=\"flex flex-col gap-0.5\">\n <span>\n {formatMoney(\n item.totalGross,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n <span className=\"text-xs font-normal text-muted-foreground\">\n {t(\"sales.documents.items.table.gross\", \"gross\")}\n </span>\n </span>\n <span className=\"text-xs font-medium text-muted-foreground\">\n {formatMoney(\n item.totalNet,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n {t(\"sales.documents.items.table.net\", \"net\")}\n </span>\n </div>\n </td>\n {injectedColumns.map((col) => {\n const colValue = resolveInjectedColumnValue(\n item as unknown as Record<string, unknown>,\n col.accessorKey,\n );\n const Cell = col.cell;\n return (\n <td\n key={col.id}\n className=\"px-3 py-3\"\n onClick={(e) => e.stopPropagation()}\n >\n {Cell ? <Cell getValue={() => colValue} /> : null}\n </td>\n );\n })}\n <td className=\"px-3 py-3\">\n <div className=\"flex items-center gap-2 justify-end\">\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-8 w-8\"\n aria-label={t('ui.actions.edit', 'Edit')}\n disabled={amountsReadOnly}\n onClick={(event) => {\n event.stopPropagation();\n handleEdit(item);\n }}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <span\n title={amountsReadOnly\n ? t('sales.documents.amountsExternalHint', 'Totals on this document come from an external system and are stored as supplied.')\n : kind === 'order' && items.length === 1\n ? t('sales.documents.items.errorDeleteLast', 'An order must contain at least one line item.')\n : undefined}\n >\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-8 w-8 text-destructive\"\n aria-label={t('ui.actions.delete', 'Delete')}\n disabled={amountsReadOnly || (kind === 'order' && items.length === 1)}\n onClick={(event) => {\n event.stopPropagation();\n void handleDelete(item);\n }}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n )}\n\n <LineItemDialog\n open={dialogOpen}\n onOpenChange={(next) => {\n setDialogOpen(next);\n if (!next) setLineForEdit(null);\n }}\n kind={kind}\n documentId={documentId}\n currencyCode={currencyCode}\n documentUpdatedAt={documentUpdatedAt}\n organizationId={resolvedOrganizationId}\n tenantId={resolvedTenantId}\n initialLine={lineForEdit}\n shippedQuantity={\n lineForEdit\n ? Math.max(0, shippedTotals.get(lineForEdit.id) ?? 0)\n : 0\n }\n shippedQuantityResolved={shippedTotalsResolved}\n onSaved={async () => {\n await loadItems();\n emitSalesDocumentTotalsRefresh({ documentId, kind });\n }}\n />\n {ConfirmDialogElement}\n </div>\n </OrderItemsInjectionContext.Provider>\n );\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport * as React from \"react\";\nimport { extensionPoints } from \"@open-mercato/core/modules/sales/extension-points\";\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 { normalizeCrudServerError } from \"@open-mercato/ui/backend/utils/serverErrors\";\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from \"@open-mercato/ui/backend/detail\";\nimport { IconButton } from \"@open-mercato/ui/primitives/icon-button\";\nimport { SimpleTooltip } from \"@open-mercato/ui/primitives/tooltip\";\nimport { flash } from \"@open-mercato/ui/backend/FlashMessages\";\nimport { Pencil, Trash2 } from \"lucide-react\";\nimport {\n DictionaryValue,\n type DictionaryMap,\n createDictionaryMap,\n normalizeDictionaryEntries,\n} from \"@open-mercato/core/modules/dictionaries/components/dictionaryAppearance\";\nimport { cn } from \"@open-mercato/shared/lib/utils\";\nimport { useT, useLocale } from \"@open-mercato/shared/lib/i18n/context\";\nimport { useOrganizationScopeDetail } from \"@open-mercato/shared/lib/frontend/useOrganizationScope\";\nimport { useConfirmDialog } from \"@open-mercato/ui/backend/confirm-dialog\";\nimport { emitSalesDocumentTotalsRefresh } from \"@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents\";\nimport { LineItemDialog } from \"./LineItemDialog\";\nimport { handleSectionMutationError } from \"./optimisticLock\";\nimport { useSalesDocumentMutation } from \"./useSalesDocumentMutation\";\nimport type { SalesLineRecord } from \"./lineItemTypes\";\nimport {\n formatMoney,\n normalizeNumber,\n resolveLineDiscountDisplay,\n} from \"./lineItemUtils\";\nimport type { SectionAction } from \"@open-mercato/ui/backend/detail\";\nimport { extractCustomFieldValues } from \"./customFieldHelpers\";\nimport { canonicalizeUnitCode } from \"@open-mercato/shared/lib/units/unitCodes\";\nimport type { SalesLineUomSnapshot } from \"../../lib/types\";\nimport { useInjectionDataWidgets } from \"@open-mercato/ui/backend/injection/useInjectionDataWidgets\";\nimport type { InjectionColumnDefinition } from \"@open-mercato/shared/modules/widgets/injection\";\nimport { OrderItemsInjectionContext } from \"../../widgets/injection/order-items-context\";\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\nfunction DisabledActionHint({\n label,\n reason,\n children,\n}: {\n label: string\n reason?: string\n children: React.ReactNode\n}) {\n const [open, setOpen] = React.useState(false)\n const touchOpenRef = React.useRef(false)\n if (!reason) return <>{children}</>\n return (\n <SimpleTooltip\n content={reason}\n open={open}\n onOpenChange={(nextOpen) => {\n if (nextOpen || !touchOpenRef.current) setOpen(nextOpen)\n }}\n >\n <span\n className=\"inline-flex rounded-md focus-visible:outline-none focus-visible:shadow-focus\"\n tabIndex={0}\n aria-label={`${label}: ${reason}`}\n onClick={(event) => event.stopPropagation()}\n onPointerDown={(event) => {\n if (event.pointerType !== 'touch') return\n touchOpenRef.current = true\n setOpen(true)\n event.currentTarget.focus()\n }}\n onBlur={() => {\n touchOpenRef.current = false\n setOpen(false)\n }}\n onKeyDown={(event) => {\n if (event.key !== 'Escape') return\n touchOpenRef.current = false\n setOpen(false)\n }}\n >\n {children}\n </span>\n </SimpleTooltip>\n )\n}\n\ntype ResolvedUnitPriceReference = {\n grossPerReference: number;\n referenceUnitCode: string;\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isSalesLineUomSnapshot(\n value: unknown,\n): value is SalesLineUomSnapshot {\n if (!isPlainObject(value)) return false;\n return (\n value.version === 1 &&\n typeof value.enteredQuantity === \"string\" &&\n typeof value.toBaseFactor === \"string\" &&\n typeof value.normalizedQuantity === \"string\"\n );\n}\n\nfunction extractUomSnapshot(\n item: Record<string, unknown>,\n): SalesLineUomSnapshot | null {\n const raw = item.uom_snapshot ?? item.uomSnapshot;\n if (isSalesLineUomSnapshot(raw)) return raw;\n return null;\n}\n\nfunction resolveUnitPriceReference(\n snapshot: SalesLineUomSnapshot | Record<string, unknown> | null,\n): ResolvedUnitPriceReference | null {\n if (!snapshot) return null;\n\n const ref = isSalesLineUomSnapshot(snapshot)\n ? snapshot.unitPriceReference\n : isPlainObject(snapshot)\n ? isPlainObject(snapshot.unitPriceReference)\n ? snapshot.unitPriceReference\n : isPlainObject(snapshot.unit_price_reference)\n ? snapshot.unit_price_reference\n : null\n : null;\n\n if (!ref || !isPlainObject(ref)) return null;\n\n const refRecord = ref as Record<string, unknown>;\n const grossPerReference = normalizeNumber(\n refRecord.grossPerReference ?? refRecord.gross_per_reference,\n Number.NaN,\n );\n if (!Number.isFinite(grossPerReference)) return null;\n\n const referenceUnitCode =\n typeof refRecord.referenceUnitCode === \"string\"\n ? refRecord.referenceUnitCode\n : typeof refRecord.reference_unit_code === \"string\"\n ? refRecord.reference_unit_code\n : typeof refRecord.referenceUnit === \"string\"\n ? refRecord.referenceUnit\n : null;\n if (!referenceUnitCode) return null;\n\n return { grossPerReference, referenceUnitCode };\n}\n\nfunction getUomFields(item: Record<string, unknown>) {\n const uomSnapshot = extractUomSnapshot(item);\n return {\n normalizedQuantity: (item.normalized_quantity ?? item.normalizedQuantity ?? null) as\n | number\n | string\n | null,\n normalizedUnit: (item.normalized_unit ?? item.normalizedUnit ?? null) as string | null,\n quantityUnit: (item.quantity_unit ?? item.quantityUnit ?? null) as string | null,\n uomSnapshot,\n };\n}\n\nfunction resolveInjectedColumnValue(\n row: Record<string, unknown>,\n accessorKey: string,\n): unknown {\n const segments = accessorKey.split(\".\").filter(Boolean);\n let current: unknown = row;\n for (const segment of segments) {\n if (!current || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\nconst LINES_PAGE_SIZE = 100;\n// Upper bound on line pages fetched for one document, so an API that ignores `page`\n// cannot spin the loader forever.\nconst LINES_MAX_PAGES = 200;\n\nconst SHIPMENTS_PAGE_SIZE = 100;\n// A single order beyond this many shipment pages is pathological; stopping there\n// keeps the shipped state explicitly unresolved rather than silently partial.\nconst SHIPMENTS_MAX_PAGES = 50;\n\ntype SalesDocumentItemsSectionProps = {\n documentId: string;\n kind: \"order\" | \"quote\";\n currencyCode: string | null | undefined;\n documentUpdatedAt?: string | null;\n organizationId?: string | null;\n tenantId?: string | null;\n onActionChange?: (action: SectionAction | null) => void;\n onItemsChange?: (items: SalesLineRecord[]) => void;\n /** Disables line writes: on an external order they would all be rejected. */\n amountsReadOnly?: boolean;\n};\n\nexport function SalesDocumentItemsSection({\n documentId,\n kind,\n currencyCode,\n documentUpdatedAt,\n organizationId: orgFromProps,\n tenantId: tenantFromProps,\n onActionChange,\n onItemsChange,\n amountsReadOnly = false,\n}: SalesDocumentItemsSectionProps) {\n const t = useT();\n const runDocumentMutation = useSalesDocumentMutation(kind, documentId);\n const locale = useLocale();\n const { organizationId, tenantId } = useOrganizationScopeDetail();\n const { confirm, ConfirmDialogElement } = useConfirmDialog();\n const resolvedOrganizationId = orgFromProps ?? organizationId ?? null;\n const resolvedTenantId = tenantFromProps ?? tenantId ?? null;\n const [items, setItems] = React.useState<SalesLineRecord[]>([]);\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 [lineForEdit, setLineForEdit] = React.useState<SalesLineRecord | null>(\n null,\n );\n const [lineStatusMap, setLineStatusMap] = React.useState<DictionaryMap>({});\n const [shippedTotals, setShippedTotals] = React.useState<Map<string, number>>(\n new Map(),\n );\n // Quotes have no shipments at all, so their shipped state is known up front.\n // For orders it stays unknown until every shipment page has been read \u2014 an\n // empty map from a pending or failed load must never read as \"nothing shipped\".\n const [shippedTotalsResolved, setShippedTotalsResolved] = React.useState(\n kind !== \"order\",\n );\n\n const { widgets: allColumnWidgets, isLoading: injectedColumnsLoading, error: injectedColumnsError } = useInjectionDataWidgets(\n extensionPoints.hosts.orderItemColumns.spotId,\n );\n const columnWidgets = kind === \"order\" ? allColumnWidgets : [];\n const injectedColumns = React.useMemo<InjectionColumnDefinition[]>(() => {\n const cols: InjectionColumnDefinition[] = [];\n for (const widget of columnWidgets) {\n if (!(\"columns\" in widget)) continue;\n for (const def of (widget as { columns?: InjectionColumnDefinition[] })\n .columns ?? []) {\n cols.push(def);\n }\n }\n return cols;\n }, [columnWidgets]);\n\n const resourcePath = React.useMemo(\n () => (kind === \"order\" ? \"sales/order-lines\" : \"sales/quote-lines\"),\n [kind],\n );\n const documentKey = kind === \"order\" ? \"orderId\" : \"quoteId\";\n const lineStatusesLoaded = React.useRef(false);\n const itemsLoadedForDocument = React.useRef<string | null>(null);\n const itemsLoadSeq = React.useRef(0);\n const shipmentsLoadedForDocument = React.useRef<string | null>(null);\n const loadLineStatuses = React.useCallback(async () => {\n try {\n const params = new URLSearchParams({ page: \"1\", pageSize: \"100\" });\n const response = await apiCall<{\n items?: Array<Record<string, unknown>>;\n }>(`/api/sales/order-line-statuses?${params.toString()}`, undefined, {\n fallback: { items: [] },\n });\n const entries = normalizeDictionaryEntries(response.result?.items ?? [], { sort: false });\n setLineStatusMap(createDictionaryMap(entries));\n } catch (err) {\n logger.error('sales.document.line-statuses.load', { err });\n setLineStatusMap({});\n }\n }, []);\n\n const loadItems = React.useCallback(async () => {\n const loadSeq = ++itemsLoadSeq.current;\n const isCurrentLoad = () => loadSeq === itemsLoadSeq.current;\n setLoading(true);\n setError(null);\n try {\n const collectedItems: Array<Record<string, unknown>> = [];\n const seenIds = new Set<string>();\n let loaded = false;\n let complete = false;\n let page = 1;\n while (page <= LINES_MAX_PAGES) {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(LINES_PAGE_SIZE),\n [documentKey]: documentId,\n });\n const response = await apiCall<{\n items?: Array<Record<string, unknown>>;\n total?: unknown;\n }>(`/api/${resourcePath}?${params.toString()}`, undefined, {\n fallback: { items: [] },\n });\n if (!response.ok || !Array.isArray(response.result?.items)) {\n if (page > 1) throw new Error(\"[internal] sales line page failed to load\");\n complete = true;\n break;\n }\n loaded = true;\n const pageItems = response.result.items;\n let added = 0;\n pageItems.forEach((item) => {\n const id = typeof item.id === \"string\" ? item.id : null;\n if (id && seenIds.has(id)) return;\n if (id) seenIds.add(id);\n collectedItems.push(item);\n added += 1;\n });\n const reportedTotal = normalizeNumber(response.result?.total, Number.NaN);\n const hasMore =\n added > 0 &&\n pageItems.length >= LINES_PAGE_SIZE &&\n (!Number.isFinite(reportedTotal) || collectedItems.length < reportedTotal);\n if (!hasMore) {\n complete = true;\n break;\n }\n page += 1;\n }\n if (!complete) throw new Error(\"[internal] sales line page limit reached\");\n if (!isCurrentLoad()) return;\n if (loaded) {\n const mapped = collectedItems.flatMap<SalesLineRecord>(\n (item) => {\n const id = typeof item.id === \"string\" ? item.id : null;\n if (!id) return [];\n const taxRate = normalizeNumber(\n item.tax_rate ?? item.taxRate,\n 0,\n );\n const customFields = extractCustomFieldValues(\n item as Record<string, unknown>,\n );\n const name =\n typeof item.name === \"string\"\n ? item.name\n : typeof item.catalog_snapshot === \"object\" &&\n item.catalog_snapshot &&\n typeof (item.catalog_snapshot as Record<string, unknown>).name === \"string\"\n ? (item.catalog_snapshot as Record<string, unknown>).name as string\n : null;\n const description =\n typeof item.description === \"string\" && item.description.trim()\n ? item.description\n : null;\n const quantity = normalizeNumber(item.quantity, 0);\n const uomFields = getUomFields(item);\n const quantityUnit = canonicalizeUnitCode(uomFields.quantityUnit);\n const normalizedQuantity = normalizeNumber(\n uomFields.normalizedQuantity,\n quantity,\n );\n const normalizedUnit =\n canonicalizeUnitCode(uomFields.normalizedUnit) ?? quantityUnit;\n const uomSnapshot = uomFields.uomSnapshot;\n const unitPriceNetRaw = normalizeNumber(\n item.unit_price_net ?? item.unitPriceNet,\n Number.NaN,\n );\n const unitPriceGrossRaw = normalizeNumber(\n item.unit_price_gross ?? item.unitPriceGross,\n Number.NaN,\n );\n const unitPriceNet = Number.isFinite(unitPriceNetRaw)\n ? unitPriceNetRaw\n : Number.isFinite(unitPriceGrossRaw)\n ? unitPriceGrossRaw / (1 + taxRate / 100)\n : 0;\n const unitPriceGross = Number.isFinite(unitPriceGrossRaw)\n ? unitPriceGrossRaw\n : Number.isFinite(unitPriceNetRaw)\n ? unitPriceNetRaw * (1 + taxRate / 100)\n : 0;\n const totalNetRaw = normalizeNumber(\n item.total_net_amount ?? item.totalNetAmount,\n Number.NaN,\n );\n const totalGrossRaw = normalizeNumber(\n item.total_gross_amount ?? item.totalGrossAmount,\n Number.NaN,\n );\n const totalNet = Number.isFinite(totalNetRaw)\n ? totalNetRaw\n : unitPriceNet * quantity;\n const totalGross = Number.isFinite(totalGrossRaw)\n ? totalGrossRaw\n : unitPriceGross * quantity;\n const priceModeRaw =\n item.metadata &&\n typeof item.metadata === \"object\"\n ? (item.metadata as Record<string, unknown>).priceMode\n : null;\n const priceMode = priceModeRaw === \"net\" ? \"net\" : \"gross\";\n const customFieldSetId =\n typeof item.custom_field_set_id === \"string\"\n ? item.custom_field_set_id\n : typeof item.customFieldSetId === \"string\"\n ? item.customFieldSetId\n : null;\n const statusEntryId =\n typeof item.status_entry_id === \"string\"\n ? item.status_entry_id\n : typeof item.statusEntryId === \"string\"\n ? item.statusEntryId\n : null;\n const status = typeof item.status === \"string\" ? item.status : null;\n const record: SalesLineRecord = {\n id,\n name,\n description,\n productId:\n typeof item.product_id === \"string\" ? item.product_id : null,\n productVariantId:\n typeof item.product_variant_id === \"string\"\n ? item.product_variant_id\n : null,\n quantity,\n quantityUnit,\n normalizedQuantity,\n normalizedUnit,\n currencyCode:\n typeof item.currency_code === \"string\"\n ? item.currency_code\n : typeof currencyCode === \"string\"\n ? currencyCode\n : null,\n unitPriceNet,\n unitPriceGross,\n discountAmount: normalizeNumber(\n item.discount_amount ?? item.discountAmount,\n 0,\n ),\n discountPercent: normalizeNumber(\n item.discount_percent ?? item.discountPercent,\n 0,\n ),\n taxRate,\n totalNet,\n totalGross,\n priceMode,\n uomSnapshot,\n metadata:\n (item.metadata as Record<string, unknown> | null | undefined) ??\n null,\n catalogSnapshot:\n (item.catalog_snapshot as\n | Record<string, unknown>\n | null\n | undefined) ?? null,\n customFieldSetId,\n customFields: Object.keys(customFields).length\n ? customFields\n : null,\n status,\n statusEntryId,\n };\n return [record];\n },\n );\n setItems(mapped);\n if (onItemsChange) onItemsChange(mapped);\n } else {\n setItems([]);\n if (onItemsChange) onItemsChange([]);\n }\n } catch (err) {\n if (!isCurrentLoad()) return;\n logger.error('sales.document.items.load', { err });\n setError(t(\"sales.documents.items.errorLoad\", \"Failed to load items.\"));\n setItems([]);\n if (onItemsChange) onItemsChange([]);\n } finally {\n if (isCurrentLoad()) setLoading(false);\n }\n }, [currencyCode, documentId, documentKey, onItemsChange, resourcePath, t]);\n\n const loadShippedTotals = React.useCallback(async () => {\n if (kind !== \"order\") {\n setShippedTotals(new Map());\n setShippedTotalsResolved(true);\n return;\n }\n setShippedTotals(new Map());\n setShippedTotalsResolved(false);\n try {\n const totals = new Map<string, number>();\n let page = 1;\n let collected = 0;\n let complete = false;\n while (page <= SHIPMENTS_MAX_PAGES) {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(SHIPMENTS_PAGE_SIZE),\n orderId: documentId,\n });\n const response = await apiCall<{\n items?: Array<Record<string, unknown>>;\n total?: unknown;\n }>(`/api/sales/shipments?${params.toString()}`, undefined, {\n fallback: { items: [] },\n });\n if (!response.ok || !Array.isArray(response.result?.items)) return;\n const shipments = response.result.items;\n shipments.forEach((shipment) => {\n const entries = Array.isArray(shipment.items)\n ? (shipment.items as Array<Record<string, unknown>>)\n : [];\n entries.forEach((entry) => {\n const lineId =\n typeof entry.orderLineId === \"string\"\n ? entry.orderLineId\n : typeof entry.order_line_id === \"string\"\n ? entry.order_line_id\n : null;\n if (!lineId) return;\n const quantity = normalizeNumber(entry.quantity, 0);\n if (!Number.isFinite(quantity) || quantity <= 0) return;\n const current = totals.get(lineId) ?? 0;\n totals.set(lineId, current + quantity);\n });\n });\n collected += shipments.length;\n const reportedTotal = normalizeNumber(response.result?.total, Number.NaN);\n const hasMore =\n shipments.length >= SHIPMENTS_PAGE_SIZE &&\n (!Number.isFinite(reportedTotal) || collected < reportedTotal);\n if (!hasMore) {\n complete = true;\n break;\n }\n page += 1;\n }\n if (!complete) return;\n setShippedTotals(totals);\n setShippedTotalsResolved(true);\n } catch (err) {\n logger.error('sales.document.shipments.load', { err });\n }\n }, [documentId, kind]);\n\n React.useEffect(() => {\n if (lineStatusesLoaded.current) return;\n lineStatusesLoaded.current = true;\n void loadLineStatuses();\n }, [loadLineStatuses]);\n\n React.useEffect(() => {\n if (!documentId) return;\n if (itemsLoadedForDocument.current === documentId) return;\n itemsLoadedForDocument.current = documentId;\n void loadItems();\n }, [documentId, loadItems]);\n\n React.useEffect(() => {\n if (kind !== \"order\") {\n shipmentsLoadedForDocument.current = null;\n setShippedTotals(new Map());\n setShippedTotalsResolved(true);\n return;\n }\n const key = `${kind}:${documentId}`;\n if (shipmentsLoadedForDocument.current === key) return;\n shipmentsLoadedForDocument.current = key;\n void loadShippedTotals();\n }, [documentId, kind, loadShippedTotals]);\n\n const openCreate = React.useCallback(() => {\n setLineForEdit(null);\n setDialogOpen(true);\n }, []);\n\n React.useEffect(() => {\n if (!onActionChange) return;\n onActionChange({\n label: t(\"sales.documents.items.add\", \"Add item\"),\n onClick: openCreate,\n disabled: amountsReadOnly,\n });\n return () => onActionChange(null);\n }, [amountsReadOnly, onActionChange, openCreate, t]);\n\n const handleEdit = React.useCallback(\n (line: SalesLineRecord) => {\n if (amountsReadOnly) return;\n setLineForEdit(line);\n setDialogOpen(true);\n },\n [amountsReadOnly],\n );\n\n const resolveVariantInfo = React.useCallback((record: SalesLineRecord) => {\n const meta =\n (record.metadata as Record<string, unknown> | null | undefined) ?? null;\n const snapshot =\n (record.catalogSnapshot as Record<string, unknown> | null | undefined) ??\n null;\n const variantSnapshot =\n snapshot &&\n typeof snapshot.variant === \"object\" &&\n snapshot.variant\n ? (snapshot.variant as Record<string, unknown>)\n : null;\n const variantTitle =\n meta && typeof meta.variantTitle === \"string\"\n ? meta.variantTitle\n : variantSnapshot && typeof variantSnapshot.name === \"string\"\n ? variantSnapshot.name\n : null;\n const variantSku =\n meta && typeof meta.variantSku === \"string\"\n ? meta.variantSku\n : variantSnapshot && typeof variantSnapshot.sku === \"string\"\n ? variantSnapshot.sku\n : null;\n\n return { variantTitle, variantSku };\n }, []);\n\n const handleDelete = React.useCallback(\n async (line: SalesLineRecord) => {\n const confirmed = await confirm({\n title: t(\n \"sales.documents.items.deleteConfirm\",\n \"Delete this line item?\",\n ),\n variant: \"destructive\",\n });\n if (!confirmed) return;\n try {\n const result = await runDocumentMutation(() => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(documentUpdatedAt),\n () =>\n deleteCrud(resourcePath, {\n body: {\n id: line.id,\n [documentKey]: documentId,\n organizationId: resolvedOrganizationId ?? undefined,\n tenantId: resolvedTenantId ?? undefined,\n },\n errorMessage: t(\n \"sales.documents.items.errorDelete\",\n \"Failed to delete line.\",\n ),\n }),\n ), { id: line.id, [documentKey]: documentId });\n if (result.ok) {\n flash(t(\"sales.documents.items.deleted\", \"Line removed.\"), \"success\");\n await loadItems();\n emitSalesDocumentTotalsRefresh({ documentId, kind });\n }\n } catch (err) {\n if (handleSectionMutationError(err, t, () => void loadItems())) return;\n logger.error('sales.document.items.delete', { err });\n const normalized = normalizeCrudServerError(err);\n const fallback = t(\n \"sales.documents.items.errorDelete\",\n \"Failed to delete line.\",\n );\n flash(normalized.message || fallback, \"error\");\n }\n },\n [\n confirm,\n documentId,\n documentKey,\n documentUpdatedAt,\n kind,\n loadItems,\n resolvedOrganizationId,\n resourcePath,\n t,\n resolvedTenantId,\n runDocumentMutation,\n ],\n );\n\n const renderStatus = React.useCallback(\n (line: SalesLineRecord) => {\n const value = line.status ?? null;\n if (!value) {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"sales.documents.items.table.statusEmpty\", \"No status\")}\n </span>\n );\n }\n return (\n <DictionaryValue\n value={value}\n map={lineStatusMap}\n fallback={<span className=\"text-xs font-medium\">{value}</span>}\n className=\"text-xs font-medium\"\n iconWrapperClassName=\"inline-flex h-6 w-6 items-center justify-center rounded bg-muted text-muted-foreground\"\n iconClassName=\"h-3.5 w-3.5\"\n colorClassName=\"h-4 w-4 rounded-full border border-border/70\"\n />\n );\n },\n [lineStatusMap, t],\n );\n\n const showDiscountColumn = React.useMemo(\n () => items.some((item) => resolveLineDiscountDisplay(item) !== null),\n [items],\n );\n\n const renderImage = (record: SalesLineRecord) => {\n const meta =\n (record.metadata as Record<string, unknown> | null | undefined) ?? {};\n const snapshot =\n (record.catalogSnapshot as Record<string, unknown> | null | undefined) ??\n {};\n const productSnapshot: Record<string, unknown> =\n typeof snapshot === \"object\" && snapshot\n ? ((snapshot.product as Record<string, unknown>) ?? {})\n : {};\n const variantSnapshot: Record<string, unknown> =\n typeof snapshot === \"object\" && snapshot\n ? ((snapshot.variant as Record<string, unknown>) ?? {})\n : {};\n const productThumb =\n (meta &&\n typeof meta.productThumbnail === \"string\" &&\n meta.productThumbnail) ||\n (productSnapshot &&\n typeof productSnapshot.thumbnailUrl === \"string\" &&\n productSnapshot.thumbnailUrl) ||\n null;\n const variantThumb =\n (meta &&\n typeof meta.variantThumbnail === \"string\" &&\n meta.variantThumbnail) ||\n (variantSnapshot &&\n typeof variantSnapshot.thumbnailUrl === \"string\" &&\n variantSnapshot.thumbnailUrl) ||\n null;\n const thumbnail = variantThumb ?? productThumb;\n if (thumbnail) {\n return (\n <img\n src={thumbnail}\n alt={record.name ?? record.id}\n className=\"h-10 w-10 rounded border object-cover\"\n />\n );\n }\n return (\n <div className=\"flex h-10 w-10 items-center justify-center rounded border bg-muted text-xs text-muted-foreground\">\n N/A\n </div>\n );\n };\n\n return (\n <OrderItemsInjectionContext.Provider value={{ documentId, kind }}>\n <div className=\"space-y-4\">\n {kind === \"order\" && injectedColumnsError ? (\n <ErrorMessage label={t('ui.dataTable.extensions.error', 'Some table extensions could not load. Reload the page to retry.')} />\n ) : null}\n {loading || (kind === \"order\" && injectedColumnsLoading) ? (\n <LoadingMessage\n label={t(\"sales.documents.items.loading\", \"Loading items\u2026\")}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : error ? (\n <p className=\"text-sm text-destructive\">{error}</p>\n ) : items.length === 0 ? (\n <TabEmptyState\n title={t(\"sales.documents.items.empty\", \"No items yet.\")}\n description={t(\n \"sales.documents.items.subtitle\",\n \"Add products and configure pricing for this document.\",\n )}\n action={{\n label: t(\"sales.documents.items.add\", \"Add item\"),\n onClick: openCreate,\n disabled: amountsReadOnly,\n }}\n />\n ) : (\n <div className=\"relative overflow-x-auto rounded border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted\">\n <tr className=\"text-left\">\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.product\", \"Product\")}\n </th>\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.status\", \"Status\")}\n </th>\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.quantity\", \"Qty\")}\n </th>\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.unit\", \"Unit price\")}\n </th>\n {showDiscountColumn ? (\n <th className=\"px-3 py-2 font-medium whitespace-nowrap\">\n {t(\"sales.documents.items.table.discount\", \"Discount\")}\n </th>\n ) : null}\n <th className=\"px-3 py-2 font-medium\">\n {t(\"sales.documents.items.table.total\", \"Total\")}\n </th>\n {injectedColumns.map((col) => (\n <th\n key={col.id}\n className=\"px-3 py-2 font-medium whitespace-nowrap\"\n >\n {col.headerKey ? t(col.headerKey, col.header) : col.header}\n </th>\n ))}\n <th className=\"px-3 py-2 font-medium sr-only\">\n {t(\"sales.documents.items.table.actions\", \"Actions\")}\n </th>\n </tr>\n </thead>\n <tbody>\n {items.map((item) => {\n const meta =\n (item.metadata as\n | Record<string, unknown>\n | null\n | undefined) ?? null;\n const { variantTitle, variantSku } = resolveVariantInfo(item);\n const productSku =\n meta && typeof meta.productSku === \"string\"\n ? meta.productSku\n : null;\n const variantLabel = variantTitle ?? variantSku;\n const variantSuffix =\n variantSku && variantLabel && variantSku !== variantLabel\n ? ` \u2022 ${variantSku}`\n : \"\";\n const showProductSku =\n productSku && productSku !== variantSku ? productSku : null;\n const shippedQuantity = Math.max(\n 0,\n shippedTotals.get(item.id) ?? 0,\n );\n const quantityLabel = item.quantityUnit\n ? `${item.quantity} ${item.quantityUnit}`\n : String(item.quantity);\n const showNormalized =\n Number.isFinite(item.normalizedQuantity) &&\n item.normalizedQuantity > 0 &&\n (item.normalizedUnit ?? null) &&\n (Math.abs(item.normalizedQuantity - item.quantity) >\n 0.000001 ||\n (item.normalizedUnit ?? null) !==\n (item.quantityUnit ?? null));\n const unitPriceReference = resolveUnitPriceReference(\n item.uomSnapshot,\n );\n const discount = resolveLineDiscountDisplay(item);\n\n return (\n <tr\n key={item.id}\n className={cn(\n \"border-t transition-colors\",\n amountsReadOnly ? \"\" : \"cursor-pointer hover:bg-muted/50\",\n )}\n onClick={() => handleEdit(item)}\n >\n <td className=\"px-3 py-3\">\n <div className=\"flex items-center gap-3\">\n {renderImage(item)}\n <div className=\"min-w-0\">\n <div className=\"truncate font-medium\">\n {item.name ??\n t(\"sales.documents.items.untitled\", \"Untitled\")}\n </div>\n {variantLabel ? (\n <div className=\"text-xs text-muted-foreground truncate\">\n {variantLabel}\n {variantSuffix}\n </div>\n ) : null}\n {showProductSku ? (\n <div className=\"text-xs text-muted-foreground truncate\">\n {showProductSku}\n </div>\n ) : null}\n {item.description ? (\n <div\n className=\"text-xs text-muted-foreground line-clamp-2\"\n title={item.description}\n >\n {item.description}\n </div>\n ) : null}\n </div>\n </div>\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex items-center\">\n {renderStatus(item)}\n </div>\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex flex-col gap-0.5\">\n <span className=\"font-medium\">{quantityLabel}</span>\n {showNormalized ? (\n <span className=\"text-xs text-muted-foreground\">\n {item.normalizedQuantity} {item.normalizedUnit}\n </span>\n ) : null}\n {shippedQuantity > 0 ? (\n <span className=\"text-xs text-muted-foreground\">\n {t(\n \"sales.documents.items.table.shipped\",\n \"{{shipped}} / {{total}} shipped\",\n {\n shipped: shippedQuantity,\n total: item.quantity,\n },\n )}\n </span>\n ) : null}\n </div>\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex flex-col gap-0.5\">\n <span className=\"font-mono text-sm\">\n {formatMoney(\n item.unitPriceGross,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"sales.documents.items.table.gross\", \"gross\")}\n </span>\n </span>\n <span className=\"font-mono text-xs text-muted-foreground\">\n {formatMoney(\n item.unitPriceNet,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n {t(\"sales.documents.items.table.net\", \"net\")}\n </span>\n {unitPriceReference ? (\n <span className=\"text-xs text-muted-foreground\">\n {t(\n \"sales.documents.items.table.unitPriceReference\",\n \"{{value}} per 1 {{unit}}\",\n {\n value: formatMoney(\n unitPriceReference.grossPerReference,\n item.currencyCode ??\n currencyCode ??\n undefined,\n locale,\n ),\n unit: unitPriceReference.referenceUnitCode,\n },\n )}\n </span>\n ) : null}\n </div>\n </td>\n {showDiscountColumn ? (\n <td className=\"px-3 py-3\">\n {discount ? (\n <div className=\"flex flex-col gap-0.5\">\n {discount.amount !== null ? (\n <span className=\"font-mono text-sm\">\n {t(\n discount.amount < 0\n ? \"sales.documents.items.table.markupAmount\"\n : \"sales.documents.items.table.discountAmount\",\n discount.amount < 0 ? \"+{{value}}\" : \"\u2212{{value}}\",\n {\n value: formatMoney(\n Math.abs(discount.amount),\n item.currencyCode ??\n currencyCode ??\n undefined,\n locale,\n ),\n },\n )}\n </span>\n ) : null}\n {discount.percent !== null ? (\n <span\n className={\n discount.amount !== null\n ? \"font-mono text-xs text-muted-foreground\"\n : \"font-mono text-sm\"\n }\n >\n {t(\n \"sales.documents.items.table.discountPercent\",\n \"{{value}}%\",\n { value: discount.percent },\n )}\n </span>\n ) : null}\n </div>\n ) : null}\n </td>\n ) : null}\n <td className=\"px-3 py-3 font-semibold\">\n <div className=\"flex flex-col gap-0.5\">\n <span>\n {formatMoney(\n item.totalGross,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n <span className=\"text-xs font-normal text-muted-foreground\">\n {t(\"sales.documents.items.table.gross\", \"gross\")}\n </span>\n </span>\n <span className=\"text-xs font-medium text-muted-foreground\">\n {formatMoney(\n item.totalNet,\n item.currencyCode ?? currencyCode ?? undefined,\n locale,\n )}{\" \"}\n {t(\"sales.documents.items.table.net\", \"net\")}\n </span>\n </div>\n </td>\n {injectedColumns.map((col) => {\n const colValue = resolveInjectedColumnValue(\n item as unknown as Record<string, unknown>,\n col.accessorKey,\n );\n const Cell = col.cell;\n return (\n <td\n key={col.id}\n className=\"px-3 py-3\"\n onClick={(e) => e.stopPropagation()}\n >\n {Cell ? <Cell getValue={() => colValue} /> : null}\n </td>\n );\n })}\n <td className=\"px-3 py-3\">\n <div className=\"flex items-center gap-2 justify-end\">\n <DisabledActionHint\n label={t('ui.actions.edit', 'Edit')}\n reason={amountsReadOnly\n ? t('sales.documents.amountsExternalHint', 'Totals on this document come from an external system and are stored as supplied.')\n : undefined}\n >\n <IconButton\n type=\"button\"\n size=\"default\"\n variant=\"ghost\"\n aria-label={t('ui.actions.edit', 'Edit')}\n disabled={amountsReadOnly}\n onClick={(event) => {\n event.stopPropagation();\n handleEdit(item);\n }}\n >\n <Pencil className=\"h-4 w-4\" />\n </IconButton>\n </DisabledActionHint>\n <DisabledActionHint\n label={t('ui.actions.delete', 'Delete')}\n reason={amountsReadOnly\n ? t('sales.documents.amountsExternalHint', 'Totals on this document come from an external system and are stored as supplied.')\n : kind === 'order' && items.length === 1\n ? t('sales.documents.items.errorDeleteLast', 'An order must contain at least one line item.')\n : undefined}\n >\n <IconButton\n type=\"button\"\n size=\"default\"\n variant=\"ghost\"\n className=\"text-destructive\"\n aria-label={t('ui.actions.delete', 'Delete')}\n disabled={amountsReadOnly || (kind === 'order' && items.length === 1)}\n onClick={(event) => {\n event.stopPropagation();\n void handleDelete(item);\n }}\n >\n <Trash2 className=\"h-4 w-4\" />\n </IconButton>\n </DisabledActionHint>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n )}\n\n <LineItemDialog\n open={dialogOpen}\n onOpenChange={(next) => {\n setDialogOpen(next);\n if (!next) setLineForEdit(null);\n }}\n kind={kind}\n documentId={documentId}\n currencyCode={currencyCode}\n documentUpdatedAt={documentUpdatedAt}\n organizationId={resolvedOrganizationId}\n tenantId={resolvedTenantId}\n initialLine={lineForEdit}\n shippedQuantity={\n lineForEdit\n ? Math.max(0, shippedTotals.get(lineForEdit.id) ?? 0)\n : 0\n }\n shippedQuantityResolved={shippedTotalsResolved}\n onSaved={async () => {\n await loadItems();\n emitSalesDocumentTotalsRefresh({ documentId, kind });\n }}\n />\n {ConfirmDialogElement}\n </div>\n </OrderItemsInjectionContext.Provider>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -33,10 +33,10 @@ function percentAccountsForAmount(line, percent, amount) {
|
|
|
33
33
|
function resolveLineDiscountDisplay(line) {
|
|
34
34
|
const amount = normalizeNumber(line.discountAmount, 0);
|
|
35
35
|
const percent = normalizeNumber(line.discountPercent, 0);
|
|
36
|
-
if (amount
|
|
36
|
+
if (amount !== 0) {
|
|
37
37
|
return {
|
|
38
38
|
amount,
|
|
39
|
-
percent: percentAccountsForAmount(line, percent, amount) ? percent : null
|
|
39
|
+
percent: amount > 0 && percentAccountsForAmount(line, percent, amount) ? percent : null
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
if (percent > 0) return { amount: null, percent };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sales/components/documents/lineItemUtils.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nexport function normalizeNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return fallback\n}\n\n/**\n * A catalog price is only usable on a line when it carries a positive amount, because line\n * submission rejects a unit price of zero. Returns the amount for the requested mode, falling\n * back to the other mode only when the requested amount is missing, or `null` when the price\n * has no positive amount to use.\n */\nexport function resolveCatalogPriceAmount(\n amountNet: number | null | undefined,\n amountGross: number | null | undefined,\n mode: 'net' | 'gross',\n): number | null {\n const isPresent = (value: number | null | undefined): value is number =>\n typeof value === 'number' && Number.isFinite(value)\n const [preferred, fallback] = mode === 'net' ? [amountNet, amountGross] : [amountGross, amountNet]\n const amount = isPresent(preferred) ? preferred : isPresent(fallback) ? fallback : null\n return amount !== null && amount > 0 ? amount : null\n}\n\n/**\n * Pass `locale` to format in the application locale (`useLocale()` in client components).\n * Omitting it keeps the runtime default, which varies per machine and is therefore not\n * assertable in tests.\n */\nexport function formatMoney(value: number, currency: string | null | undefined, locale?: string): string {\n if (!currency) return value.toFixed(2)\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value)\n}\n\nexport type LineDiscountDisplay = {\n amount: number | null\n percent: number | null\n}\n\ntype LineDiscountSource = {\n discountAmount?: unknown\n discountPercent?: unknown\n unitPriceNet?: unknown\n quantity?: unknown\n}\n\nconst DISCOUNT_MATCH_TOLERANCE = 0.01\nconst DISCOUNT_MATCH_TOLERANCE_PER_UNIT = 0.0001\n\nfunction percentAccountsForAmount(\n line: LineDiscountSource,\n percent: number,\n amount: number,\n): boolean {\n if (percent <= 0) return false\n const quantity = normalizeNumber(line.quantity, 0)\n const netBeforeDiscount = normalizeNumber(line.unitPriceNet, 0) * quantity\n if (netBeforeDiscount <= 0) return false\n const tolerance = Math.max(\n DISCOUNT_MATCH_TOLERANCE,\n quantity * DISCOUNT_MATCH_TOLERANCE_PER_UNIT,\n )\n return Math.abs((percent / 100) * netBeforeDiscount - amount) <= tolerance\n}\n\nexport function resolveLineDiscountDisplay(\n line: LineDiscountSource,\n): LineDiscountDisplay | null {\n const amount = normalizeNumber(line.discountAmount, 0)\n const percent = normalizeNumber(line.discountPercent, 0)\n if (amount
|
|
5
|
-
"mappings": ";AAEO,SAAS,gBAAgB,OAAgB,WAAW,GAAW;AACpE,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,QAAQ;AACpD,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,CAAC,OAAO,MAAM,MAAM,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAQO,SAAS,0BACd,WACA,aACA,MACe;AACf,QAAM,YAAY,CAAC,UACjB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AACpD,QAAM,CAAC,WAAW,QAAQ,IAAI,SAAS,QAAQ,CAAC,WAAW,WAAW,IAAI,CAAC,aAAa,SAAS;AACjG,QAAM,SAAS,UAAU,SAAS,IAAI,YAAY,UAAU,QAAQ,IAAI,WAAW;AACnF,SAAO,WAAW,QAAQ,SAAS,IAAI,SAAS;AAClD;AAOO,SAAS,YAAY,OAAe,UAAqC,QAAyB;AACvG,MAAI,CAAC,SAAU,QAAO,MAAM,QAAQ,CAAC;AACrC,SAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,KAAK;AACpF;AAcA,MAAM,2BAA2B;AACjC,MAAM,oCAAoC;AAE1C,SAAS,yBACP,MACA,SACA,QACS;AACT,MAAI,WAAW,EAAG,QAAO;AACzB,QAAM,WAAW,gBAAgB,KAAK,UAAU,CAAC;AACjD,QAAM,oBAAoB,gBAAgB,KAAK,cAAc,CAAC,IAAI;AAClE,MAAI,qBAAqB,EAAG,QAAO;AACnC,QAAM,YAAY,KAAK;AAAA,IACrB;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,KAAK,IAAK,UAAU,MAAO,oBAAoB,MAAM,KAAK;AACnE;AAEO,SAAS,2BACd,MAC4B;AAC5B,QAAM,SAAS,gBAAgB,KAAK,gBAAgB,CAAC;AACrD,QAAM,UAAU,gBAAgB,KAAK,iBAAiB,CAAC;AACvD,MAAI,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nexport function normalizeNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return fallback\n}\n\n/**\n * A catalog price is only usable on a line when it carries a positive amount, because line\n * submission rejects a unit price of zero. Returns the amount for the requested mode, falling\n * back to the other mode only when the requested amount is missing, or `null` when the price\n * has no positive amount to use.\n */\nexport function resolveCatalogPriceAmount(\n amountNet: number | null | undefined,\n amountGross: number | null | undefined,\n mode: 'net' | 'gross',\n): number | null {\n const isPresent = (value: number | null | undefined): value is number =>\n typeof value === 'number' && Number.isFinite(value)\n const [preferred, fallback] = mode === 'net' ? [amountNet, amountGross] : [amountGross, amountNet]\n const amount = isPresent(preferred) ? preferred : isPresent(fallback) ? fallback : null\n return amount !== null && amount > 0 ? amount : null\n}\n\n/**\n * Pass `locale` to format in the application locale (`useLocale()` in client components).\n * Omitting it keeps the runtime default, which varies per machine and is therefore not\n * assertable in tests.\n */\nexport function formatMoney(value: number, currency: string | null | undefined, locale?: string): string {\n if (!currency) return value.toFixed(2)\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value)\n}\n\nexport type LineDiscountDisplay = {\n amount: number | null\n percent: number | null\n}\n\ntype LineDiscountSource = {\n discountAmount?: unknown\n discountPercent?: unknown\n unitPriceNet?: unknown\n quantity?: unknown\n}\n\nconst DISCOUNT_MATCH_TOLERANCE = 0.01\nconst DISCOUNT_MATCH_TOLERANCE_PER_UNIT = 0.0001\n\nfunction percentAccountsForAmount(\n line: LineDiscountSource,\n percent: number,\n amount: number,\n): boolean {\n if (percent <= 0) return false\n const quantity = normalizeNumber(line.quantity, 0)\n const netBeforeDiscount = normalizeNumber(line.unitPriceNet, 0) * quantity\n if (netBeforeDiscount <= 0) return false\n const tolerance = Math.max(\n DISCOUNT_MATCH_TOLERANCE,\n quantity * DISCOUNT_MATCH_TOLERANCE_PER_UNIT,\n )\n return Math.abs((percent / 100) * netBeforeDiscount - amount) <= tolerance\n}\n\nexport function resolveLineDiscountDisplay(\n line: LineDiscountSource,\n): LineDiscountDisplay | null {\n const amount = normalizeNumber(line.discountAmount, 0)\n const percent = normalizeNumber(line.discountPercent, 0)\n if (amount !== 0) {\n return {\n amount,\n percent: amount > 0 && percentAccountsForAmount(line, percent, amount) ? percent : null,\n }\n }\n if (percent > 0) return { amount: null, percent }\n return null\n}\n"],
|
|
5
|
+
"mappings": ";AAEO,SAAS,gBAAgB,OAAgB,WAAW,GAAW;AACpE,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,QAAQ;AACpD,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,CAAC,OAAO,MAAM,MAAM,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAQO,SAAS,0BACd,WACA,aACA,MACe;AACf,QAAM,YAAY,CAAC,UACjB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AACpD,QAAM,CAAC,WAAW,QAAQ,IAAI,SAAS,QAAQ,CAAC,WAAW,WAAW,IAAI,CAAC,aAAa,SAAS;AACjG,QAAM,SAAS,UAAU,SAAS,IAAI,YAAY,UAAU,QAAQ,IAAI,WAAW;AACnF,SAAO,WAAW,QAAQ,SAAS,IAAI,SAAS;AAClD;AAOO,SAAS,YAAY,OAAe,UAAqC,QAAyB;AACvG,MAAI,CAAC,SAAU,QAAO,MAAM,QAAQ,CAAC;AACrC,SAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,KAAK;AACpF;AAcA,MAAM,2BAA2B;AACjC,MAAM,oCAAoC;AAE1C,SAAS,yBACP,MACA,SACA,QACS;AACT,MAAI,WAAW,EAAG,QAAO;AACzB,QAAM,WAAW,gBAAgB,KAAK,UAAU,CAAC;AACjD,QAAM,oBAAoB,gBAAgB,KAAK,cAAc,CAAC,IAAI;AAClE,MAAI,qBAAqB,EAAG,QAAO;AACnC,QAAM,YAAY,KAAK;AAAA,IACrB;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,KAAK,IAAK,UAAU,MAAO,oBAAoB,MAAM,KAAK;AACnE;AAEO,SAAS,2BACd,MAC4B;AAC5B,QAAM,SAAS,gBAAgB,KAAK,gBAAgB,CAAC;AACrD,QAAM,UAAU,gBAAgB,KAAK,iBAAiB,CAAC;AACvD,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,MACL;AAAA,MACA,SAAS,SAAS,KAAK,yBAAyB,MAAM,SAAS,MAAM,IAAI,UAAU;AAAA,IACrF;AAAA,EACF;AACA,MAAI,UAAU,EAAG,QAAO,EAAE,QAAQ,MAAM,QAAQ;AAChD,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.10.1-develop.
|
|
3
|
+
"version": "0.10.1-develop.7836.1.0ef0f453c6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -266,16 +266,16 @@
|
|
|
266
266
|
"zod": "^4.6.6"
|
|
267
267
|
},
|
|
268
268
|
"peerDependencies": {
|
|
269
|
-
"@open-mercato/ai-assistant": "0.10.1-develop.
|
|
270
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
271
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
269
|
+
"@open-mercato/ai-assistant": "0.10.1-develop.7836.1.0ef0f453c6",
|
|
270
|
+
"@open-mercato/shared": "0.10.1-develop.7836.1.0ef0f453c6",
|
|
271
|
+
"@open-mercato/ui": "0.10.1-develop.7836.1.0ef0f453c6",
|
|
272
272
|
"react": "^19.0.0",
|
|
273
273
|
"react-dom": "^19.0.0"
|
|
274
274
|
},
|
|
275
275
|
"devDependencies": {
|
|
276
|
-
"@open-mercato/ai-assistant": "0.10.1-develop.
|
|
277
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
278
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
276
|
+
"@open-mercato/ai-assistant": "0.10.1-develop.7836.1.0ef0f453c6",
|
|
277
|
+
"@open-mercato/shared": "0.10.1-develop.7836.1.0ef0f453c6",
|
|
278
|
+
"@open-mercato/ui": "0.10.1-develop.7836.1.0ef0f453c6",
|
|
279
279
|
"@testing-library/dom": "^10.4.1",
|
|
280
280
|
"@testing-library/jest-dom": "^7.0.1",
|
|
281
281
|
"@testing-library/react": "^16.3.3",
|
|
@@ -184,7 +184,7 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
184
184
|
{ status: 409, description: 'Optimistic-lock conflict' },
|
|
185
185
|
{ status: 422, description: 'Idempotency key mismatch' },
|
|
186
186
|
{ status: 429, description: 'Rate limited' },
|
|
187
|
-
{ status: 503, description: 'No store context resolvable for
|
|
187
|
+
{ status: 503, description: 'No store context resolvable, or cart_busy after 15 seconds waiting for the line lock. The requested line add/bulk-add phases did not run; earlier activity/context/repricing may have persisted. Retry the same request after Retry-After: 1 second, preserving Idempotency-Key.' },
|
|
188
188
|
],
|
|
189
189
|
},
|
|
190
190
|
},
|
|
@@ -201,7 +201,7 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
201
201
|
{ status: 409, description: 'Optimistic-lock conflict, or the cart is a `proposal` cart' },
|
|
202
202
|
{ status: 422, description: 'Quantity rule violation, idempotency key mismatch, or an unpriceable product' },
|
|
203
203
|
{ status: 429, description: 'Rate limited' },
|
|
204
|
-
{ status: 503, description: 'No store context resolvable for
|
|
204
|
+
{ status: 503, description: 'No store context resolvable, or cart_busy after 15 seconds waiting for the line lock. The requested line add/bulk-add phases did not run; earlier activity/context/repricing may have persisted. Retry the same request after Retry-After: 1 second, preserving Idempotency-Key.' },
|
|
205
205
|
],
|
|
206
206
|
},
|
|
207
207
|
},
|
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
} from '../lib/visibility'
|
|
58
58
|
import type { CartVisibilityLineRef, ResolvedAssortmentScope } from '../lib/visibility'
|
|
59
59
|
import { shouldEnforceAssortmentScope } from '../lib/routeContext'
|
|
60
|
+
import { withCartLineMutationLock } from '../lib/lineMutationLock'
|
|
60
61
|
import type { ResolvedCartRouteContext } from '../lib/routeContext'
|
|
61
62
|
import { buildRecomputeCartTotalsPhase } from '../lib/reprice'
|
|
62
63
|
import type { CartPriceChange } from '../lib/reprice'
|
|
@@ -938,10 +939,7 @@ const addLineCommand: CommandHandler<CartLinesAddInput, CartLinesAddOutcome> = {
|
|
|
938
939
|
// `cart.lines.add`/`.bulkAdd` call against this cart instead, closing the race
|
|
939
940
|
// `TC-CART-CONC-05` reproduced (a silently lost update, and a raw 500 off
|
|
940
941
|
// `cart_lines_unique_line_key`).
|
|
941
|
-
await
|
|
942
|
-
async () => {
|
|
943
|
-
await em.execute('select pg_advisory_xact_lock(hashtextextended(?::text, 0))', [`cart:lines:${cart.id}`])
|
|
944
|
-
},
|
|
942
|
+
await withCartLineMutationLock(em, cart.id, [
|
|
945
943
|
async () => {
|
|
946
944
|
const existing = await findExistingCustomerLine(em, scope, cart.id, input.productId, variantId, input.configuration ?? null)
|
|
947
945
|
const pricingContext = buildCartPricingRowFilterContext(cart)
|
|
@@ -1049,7 +1047,7 @@ const addLineCommand: CommandHandler<CartLinesAddInput, CartLinesAddOutcome> = {
|
|
|
1049
1047
|
if (!shouldRecompute()) return
|
|
1050
1048
|
await phase()
|
|
1051
1049
|
}),
|
|
1052
|
-
],
|
|
1050
|
+
], 'cart.lines.add')
|
|
1053
1051
|
|
|
1054
1052
|
if (!outcome) throw new Error('[internal] cart.lines.add produced no outcome')
|
|
1055
1053
|
// Captured into plain `const`s (never reassigned themselves) rather than read directly
|
|
@@ -1641,10 +1639,7 @@ const bulkAddLinesCommand: CommandHandler<CartLinesBulkAddInput, CartLinesBulkAd
|
|
|
1641
1639
|
// decision is read-decide-write against rows that may not exist yet, so two concurrent
|
|
1642
1640
|
// `bulkAdd`/`add` calls on this cart must serialize, not just this call's own entries
|
|
1643
1641
|
// against each other.
|
|
1644
|
-
await
|
|
1645
|
-
async () => {
|
|
1646
|
-
await em.execute('select pg_advisory_xact_lock(hashtextextended(?::text, 0))', [`cart:lines:${cart.id}`])
|
|
1647
|
-
},
|
|
1642
|
+
await withCartLineMutationLock(em, cart.id, [
|
|
1648
1643
|
...phases,
|
|
1649
1644
|
...buildCartPromotionsAndTotalsPhases({
|
|
1650
1645
|
em,
|
|
@@ -1657,7 +1652,7 @@ const bulkAddLinesCommand: CommandHandler<CartLinesBulkAddInput, CartLinesBulkAd
|
|
|
1657
1652
|
removedGiftLines,
|
|
1658
1653
|
promotionWarnings,
|
|
1659
1654
|
}),
|
|
1660
|
-
],
|
|
1655
|
+
], 'cart.lines.bulkAdd')
|
|
1661
1656
|
await emitVisibilityRejectedEvent(cart, visibilityRejectedCount, 'lines.bulkAdd')
|
|
1662
1657
|
|
|
1663
1658
|
const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
|