@open-mercato/core 0.4.5-develop-f4858e0ef3 → 0.4.5-develop-4849712ccb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/entities/catalog_product/index.js +16 -0
- package/dist/generated/entities/catalog_product/index.js.map +2 -2
- package/dist/generated/entities/catalog_product_unit_conversion/index.js +27 -0
- package/dist/generated/entities/catalog_product_unit_conversion/index.js.map +7 -0
- package/dist/generated/entities/sales_credit_memo_line/index.js +7 -1
- package/dist/generated/entities/sales_credit_memo_line/index.js.map +2 -2
- package/dist/generated/entities/sales_invoice_line/index.js +7 -1
- package/dist/generated/entities/sales_invoice_line/index.js.map +2 -2
- package/dist/generated/entities/sales_order_line/index.js +6 -0
- package/dist/generated/entities/sales_order_line/index.js.map +2 -2
- package/dist/generated/entities/sales_quote_line/index.js +6 -0
- package/dist/generated/entities/sales_quote_line/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/catalog/api/prices/route.js +123 -8
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/product-unit-conversions/route.js +194 -0
- package/dist/modules/catalog/api/product-unit-conversions/route.js.map +7 -0
- package/dist/modules/catalog/api/products/route.js +351 -201
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +1267 -497
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +733 -210
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/commands/index.js +1 -0
- package/dist/modules/catalog/commands/index.js.map +2 -2
- package/dist/modules/catalog/commands/productUnitConversions.js +503 -0
- package/dist/modules/catalog/commands/productUnitConversions.js.map +7 -0
- package/dist/modules/catalog/commands/products.js +355 -73
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/commands/shared.js +18 -4
- package/dist/modules/catalog/commands/shared.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductUomSection.js +591 -0
- package/dist/modules/catalog/components/products/ProductUomSection.js.map +7 -0
- package/dist/modules/catalog/components/products/productForm.js +66 -5
- package/dist/modules/catalog/components/products/productForm.js.map +2 -2
- package/dist/modules/catalog/components/products/productFormUtils.js +68 -0
- package/dist/modules/catalog/components/products/productFormUtils.js.map +7 -0
- package/dist/modules/catalog/data/entities.js +86 -0
- package/dist/modules/catalog/data/entities.js.map +2 -2
- package/dist/modules/catalog/data/validators.js +65 -3
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/events.js +3 -0
- package/dist/modules/catalog/events.js.map +2 -2
- package/dist/modules/catalog/lib/unitCodes.js +7 -0
- package/dist/modules/catalog/lib/unitCodes.js.map +7 -0
- package/dist/modules/catalog/lib/unitResolution.js +53 -0
- package/dist/modules/catalog/lib/unitResolution.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260218225422.js +19 -0
- package/dist/modules/catalog/migrations/Migration20260218225422.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260219084500.js +27 -0
- package/dist/modules/catalog/migrations/Migration20260219084500.js.map +7 -0
- package/dist/modules/catalog/search.js +69 -1
- package/dist/modules/catalog/search.js.map +2 -2
- package/dist/modules/catalog/seed/examples.js +91 -42
- package/dist/modules/catalog/seed/examples.js.map +2 -2
- package/dist/modules/dashboards/seed/analytics.js +3 -0
- package/dist/modules/dashboards/seed/analytics.js.map +2 -2
- package/dist/modules/sales/api/order-lines/route.js +98 -15
- package/dist/modules/sales/api/order-lines/route.js.map +2 -2
- package/dist/modules/sales/api/quote-lines/route.js +101 -14
- package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/public/[token]/route.js +87 -12
- package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +1424 -260
- package/dist/modules/sales/commands/documents.js.map +3 -3
- package/dist/modules/sales/commands/shared.js +6 -2
- package/dist/modules/sales/commands/shared.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +216 -86
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +913 -241
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +3 -3
- package/dist/modules/sales/components/documents/ShipmentsSection.js +15 -3
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/data/entities.js +59 -3
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/data/validators.js +35 -0
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/sales/frontend/quote/[token]/page.js +15 -1
- package/dist/modules/sales/frontend/quote/[token]/page.js.map +2 -2
- package/dist/modules/sales/migrations/Migration20260218225423.js +31 -0
- package/dist/modules/sales/migrations/Migration20260218225423.js.map +7 -0
- package/dist/modules/sales/migrations/Migration20260219084501.js +71 -0
- package/dist/modules/sales/migrations/Migration20260219084501.js.map +7 -0
- package/dist/modules/sales/search.js +28 -0
- package/dist/modules/sales/search.js.map +2 -2
- package/dist/modules/sales/seed/examples.js +14 -1
- package/dist/modules/sales/seed/examples.js.map +2 -2
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js +1 -1
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +2 -2
- package/generated/entities/catalog_product/index.ts +8 -0
- package/generated/entities/catalog_product_unit_conversion/index.ts +12 -0
- package/generated/entities/sales_credit_memo_line/index.ts +3 -0
- package/generated/entities/sales_invoice_line/index.ts +3 -0
- package/generated/entities/sales_order_line/index.ts +3 -0
- package/generated/entities/sales_quote_line/index.ts +3 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +2 -2
- package/src/modules/auth/i18n/de.json +1 -1
- package/src/modules/auth/i18n/en.json +1 -1
- package/src/modules/auth/i18n/es.json +1 -1
- package/src/modules/auth/i18n/pl.json +1 -1
- package/src/modules/catalog/api/prices/route.ts +213 -81
- package/src/modules/catalog/api/product-unit-conversions/route.ts +195 -0
- package/src/modules/catalog/api/products/route.ts +638 -402
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2085 -1072
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +1288 -593
- package/src/modules/catalog/commands/index.ts +1 -0
- package/src/modules/catalog/commands/productUnitConversions.ts +626 -0
- package/src/modules/catalog/commands/products.ts +1151 -693
- package/src/modules/catalog/commands/shared.ts +19 -5
- package/src/modules/catalog/components/products/ProductUomSection.tsx +745 -0
- package/src/modules/catalog/components/products/productForm.ts +369 -256
- package/src/modules/catalog/components/products/productFormUtils.ts +82 -0
- package/src/modules/catalog/data/entities.ts +82 -1
- package/src/modules/catalog/data/validators.ts +118 -34
- package/src/modules/catalog/events.ts +3 -0
- package/src/modules/catalog/i18n/de.json +56 -0
- package/src/modules/catalog/i18n/en.json +56 -0
- package/src/modules/catalog/i18n/es.json +56 -0
- package/src/modules/catalog/i18n/pl.json +56 -0
- package/src/modules/catalog/lib/unitCodes.ts +1 -0
- package/src/modules/catalog/lib/unitResolution.ts +62 -0
- package/src/modules/catalog/migrations/.snapshot-open-mercato.json +245 -0
- package/src/modules/catalog/migrations/Migration20260218225422.ts +21 -0
- package/src/modules/catalog/migrations/Migration20260219084500.ts +26 -0
- package/src/modules/catalog/search.ts +73 -1
- package/src/modules/catalog/seed/examples.ts +552 -479
- package/src/modules/dashboards/i18n/de.json +1 -1
- package/src/modules/dashboards/i18n/en.json +1 -1
- package/src/modules/dashboards/i18n/es.json +1 -1
- package/src/modules/dashboards/i18n/pl.json +1 -1
- package/src/modules/dashboards/seed/analytics.ts +3 -0
- package/src/modules/sales/api/order-lines/route.ts +158 -68
- package/src/modules/sales/api/quote-lines/route.ts +161 -67
- package/src/modules/sales/api/quotes/public/[token]/route.ts +122 -36
- package/src/modules/sales/commands/documents.ts +4250 -2424
- package/src/modules/sales/commands/shared.ts +7 -2
- package/src/modules/sales/components/documents/ItemsSection.tsx +580 -310
- package/src/modules/sales/components/documents/LineItemDialog.tsx +1988 -833
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +17 -3
- package/src/modules/sales/components/documents/lineItemTypes.ts +6 -0
- package/src/modules/sales/data/entities.ts +53 -0
- package/src/modules/sales/data/validators.ts +36 -0
- package/src/modules/sales/frontend/quote/[token]/page.tsx +25 -1
- package/src/modules/sales/i18n/de.json +23 -3
- package/src/modules/sales/i18n/en.json +23 -3
- package/src/modules/sales/i18n/es.json +23 -3
- package/src/modules/sales/i18n/pl.json +23 -3
- package/src/modules/sales/lib/types.ts +30 -0
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +172 -0
- package/src/modules/sales/migrations/Migration20260218225423.ts +37 -0
- package/src/modules/sales/migrations/Migration20260219084501.ts +73 -0
- package/src/modules/sales/search.ts +28 -0
- package/src/modules/sales/seed/examples.ts +20 -1
- package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +1 -1
- package/src/modules/workflows/i18n/de.json +4 -4
- package/src/modules/workflows/i18n/en.json +4 -4
- package/src/modules/workflows/i18n/es.json +4 -4
- package/src/modules/workflows/i18n/pl.json +4 -4
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
2
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
2
3
|
import { assertFound } from "@open-mercato/shared/lib/crud/errors";
|
|
3
4
|
import { ensureOrganizationScope, ensureSameScope, ensureTenantScope } from "@open-mercato/shared/lib/commands/scope";
|
|
4
5
|
import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
|
|
@@ -10,8 +11,11 @@ function toNumericString(value) {
|
|
|
10
11
|
if (value === void 0 || value === null) return null;
|
|
11
12
|
return value.toString();
|
|
12
13
|
}
|
|
13
|
-
async function requireScopedEntity(em, entityClass, id, message) {
|
|
14
|
-
const
|
|
14
|
+
async function requireScopedEntity(em, entityClass, id, message, scope = { organizationId: null, tenantId: null }) {
|
|
15
|
+
const where = { id, deletedAt: null };
|
|
16
|
+
if (scope.organizationId) where.organizationId = scope.organizationId;
|
|
17
|
+
if (scope.tenantId) where.tenantId = scope.tenantId;
|
|
18
|
+
const entity = await findOneWithDecryption(em, entityClass, where, {}, scope);
|
|
15
19
|
if (!entity) throw new CrudHttpError(404, { error: message });
|
|
16
20
|
return entity;
|
|
17
21
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sales/commands/shared.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nexport { assertFound } from '@open-mercato/shared/lib/crud/errors'\nexport { ensureOrganizationScope, ensureSameScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'\nexport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\n\nexport function cloneJson<T>(value: T): T {\n if (value === null || value === undefined) return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nexport function toNumericString(value: number | null | undefined): string | null {\n if (value === undefined || value === null) return null\n return value.toString()\n}\n\nexport async function requireScopedEntity<T extends { id: string; deletedAt?: Date | null }>(\n em: EntityManager,\n entityClass: { new (): T },\n id: string,\n message: string
|
|
5
|
-
"mappings": "AACA,SAAS,qBAAqB;
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nexport { assertFound } from '@open-mercato/shared/lib/crud/errors'\nexport { ensureOrganizationScope, ensureSameScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'\nexport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\n\nexport function cloneJson<T>(value: T): T {\n if (value === null || value === undefined) return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nexport function toNumericString(value: number | null | undefined): string | null {\n if (value === undefined || value === null) return null\n return value.toString()\n}\n\nexport async function requireScopedEntity<T extends { id: string; deletedAt?: Date | null }>(\n em: EntityManager,\n entityClass: { new (): T },\n id: string,\n message: string,\n scope: { organizationId: string | null; tenantId: string | null } = { organizationId: null, tenantId: null },\n): Promise<T> {\n const where: Record<string, unknown> = { id, deletedAt: null }\n if (scope.organizationId) where.organizationId = scope.organizationId\n if (scope.tenantId) where.tenantId = scope.tenantId\n const entity = await findOneWithDecryption(em, entityClass, where, {}, scope)\n if (!entity) throw new CrudHttpError(404, { error: message })\n return entity\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AAEtC,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB,iBAAiB,yBAAyB;AAC5E,SAAS,0BAA0B;AAE5B,SAAS,UAAa,OAAa;AACxC,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,SAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;AAEO,SAAS,gBAAgB,OAAiD;AAC/E,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,SAAO,MAAM,SAAS;AACxB;AAEA,eAAsB,oBACpB,IACA,aACA,IACA,SACA,QAAoE,EAAE,gBAAgB,MAAM,UAAU,KAAK,GAC/F;AACZ,QAAM,QAAiC,EAAE,IAAI,WAAW,KAAK;AAC7D,MAAI,MAAM,eAAgB,OAAM,iBAAiB,MAAM;AACvD,MAAI,MAAM,SAAU,OAAM,WAAW,MAAM;AAC3C,QAAM,SAAS,MAAM,sBAAsB,IAAI,aAAa,OAAO,CAAC,GAAG,KAAK;AAC5E,MAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,QAAQ,CAAC;AAC5D,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,6 +20,42 @@ import { emitSalesDocumentTotalsRefresh } from "@open-mercato/core/modules/sales
|
|
|
20
20
|
import { LineItemDialog } from "./LineItemDialog.js";
|
|
21
21
|
import { formatMoney, normalizeNumber } from "./lineItemUtils.js";
|
|
22
22
|
import { extractCustomFieldValues } from "./customFieldHelpers.js";
|
|
23
|
+
import { canonicalizeUnitCode } from "@open-mercato/shared/lib/units/unitCodes";
|
|
24
|
+
function isPlainObject(value) {
|
|
25
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
function isSalesLineUomSnapshot(value) {
|
|
28
|
+
if (!isPlainObject(value)) return false;
|
|
29
|
+
return value.version === 1 && typeof value.enteredQuantity === "string" && typeof value.toBaseFactor === "string" && typeof value.normalizedQuantity === "string";
|
|
30
|
+
}
|
|
31
|
+
function extractUomSnapshot(item) {
|
|
32
|
+
const raw = item.uom_snapshot ?? item.uomSnapshot;
|
|
33
|
+
if (isSalesLineUomSnapshot(raw)) return raw;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function resolveUnitPriceReference(snapshot) {
|
|
37
|
+
if (!snapshot) return null;
|
|
38
|
+
const ref = isSalesLineUomSnapshot(snapshot) ? snapshot.unitPriceReference : isPlainObject(snapshot) ? isPlainObject(snapshot.unitPriceReference) ? snapshot.unitPriceReference : isPlainObject(snapshot.unit_price_reference) ? snapshot.unit_price_reference : null : null;
|
|
39
|
+
if (!ref || !isPlainObject(ref)) return null;
|
|
40
|
+
const refRecord = ref;
|
|
41
|
+
const grossPerReference = normalizeNumber(
|
|
42
|
+
refRecord.grossPerReference ?? refRecord.gross_per_reference,
|
|
43
|
+
Number.NaN
|
|
44
|
+
);
|
|
45
|
+
if (!Number.isFinite(grossPerReference)) return null;
|
|
46
|
+
const referenceUnitCode = typeof refRecord.referenceUnitCode === "string" ? refRecord.referenceUnitCode : typeof refRecord.reference_unit_code === "string" ? refRecord.reference_unit_code : typeof refRecord.referenceUnit === "string" ? refRecord.referenceUnit : null;
|
|
47
|
+
if (!referenceUnitCode) return null;
|
|
48
|
+
return { grossPerReference, referenceUnitCode };
|
|
49
|
+
}
|
|
50
|
+
function getUomFields(item) {
|
|
51
|
+
const uomSnapshot = extractUomSnapshot(item);
|
|
52
|
+
return {
|
|
53
|
+
normalizedQuantity: item.normalized_quantity ?? item.normalizedQuantity ?? null,
|
|
54
|
+
normalizedUnit: item.normalized_unit ?? item.normalizedUnit ?? null,
|
|
55
|
+
quantityUnit: item.quantity_unit ?? item.quantityUnit ?? null,
|
|
56
|
+
uomSnapshot
|
|
57
|
+
};
|
|
58
|
+
}
|
|
23
59
|
function SalesDocumentItemsSection({
|
|
24
60
|
documentId,
|
|
25
61
|
kind,
|
|
@@ -38,9 +74,13 @@ function SalesDocumentItemsSection({
|
|
|
38
74
|
const [loading, setLoading] = React.useState(false);
|
|
39
75
|
const [error, setError] = React.useState(null);
|
|
40
76
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
41
|
-
const [lineForEdit, setLineForEdit] = React.useState(
|
|
77
|
+
const [lineForEdit, setLineForEdit] = React.useState(
|
|
78
|
+
null
|
|
79
|
+
);
|
|
42
80
|
const [lineStatusMap, setLineStatusMap] = React.useState({});
|
|
43
|
-
const [shippedTotals, setShippedTotals] = React.useState(
|
|
81
|
+
const [shippedTotals, setShippedTotals] = React.useState(
|
|
82
|
+
/* @__PURE__ */ new Map()
|
|
83
|
+
);
|
|
44
84
|
const resourcePath = React.useMemo(
|
|
45
85
|
() => kind === "order" ? "sales/order-lines" : "sales/quote-lines",
|
|
46
86
|
[kind]
|
|
@@ -52,11 +92,9 @@ function SalesDocumentItemsSection({
|
|
|
52
92
|
const loadLineStatuses = React.useCallback(async () => {
|
|
53
93
|
try {
|
|
54
94
|
const params = new URLSearchParams({ page: "1", pageSize: "100" });
|
|
55
|
-
const response = await apiCall(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{ fallback: { items: [] } }
|
|
59
|
-
);
|
|
95
|
+
const response = await apiCall(`/api/sales/order-line-statuses?${params.toString()}`, void 0, {
|
|
96
|
+
fallback: { items: [] }
|
|
97
|
+
});
|
|
60
98
|
const entries = normalizeDictionaryEntries(response.result?.items ?? []);
|
|
61
99
|
setLineStatusMap(createDictionaryMap(entries));
|
|
62
100
|
} catch (err) {
|
|
@@ -68,64 +106,88 @@ function SalesDocumentItemsSection({
|
|
|
68
106
|
setLoading(true);
|
|
69
107
|
setError(null);
|
|
70
108
|
try {
|
|
71
|
-
const params = new URLSearchParams({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
109
|
+
const params = new URLSearchParams({
|
|
110
|
+
page: "1",
|
|
111
|
+
pageSize: "100",
|
|
112
|
+
[documentKey]: documentId
|
|
113
|
+
});
|
|
114
|
+
const response = await apiCall(`/api/${resourcePath}?${params.toString()}`, void 0, {
|
|
115
|
+
fallback: { items: [] }
|
|
116
|
+
});
|
|
77
117
|
if (response.ok && Array.isArray(response.result?.items)) {
|
|
78
|
-
const mapped = response.result.items.flatMap(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
totalNet
|
|
118
|
-
totalGross
|
|
119
|
-
priceMode
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
const mapped = response.result.items.flatMap(
|
|
119
|
+
(item) => {
|
|
120
|
+
const id = typeof item.id === "string" ? item.id : null;
|
|
121
|
+
if (!id) return [];
|
|
122
|
+
const taxRate = normalizeNumber(
|
|
123
|
+
item.tax_rate ?? item.taxRate,
|
|
124
|
+
0
|
|
125
|
+
);
|
|
126
|
+
const customFields = extractCustomFieldValues(
|
|
127
|
+
item
|
|
128
|
+
);
|
|
129
|
+
const name = typeof item.name === "string" ? item.name : typeof item.catalog_snapshot === "object" && item.catalog_snapshot && typeof item.catalog_snapshot.name === "string" ? item.catalog_snapshot.name : null;
|
|
130
|
+
const quantity = normalizeNumber(item.quantity, 0);
|
|
131
|
+
const uomFields = getUomFields(item);
|
|
132
|
+
const quantityUnit = canonicalizeUnitCode(uomFields.quantityUnit);
|
|
133
|
+
const normalizedQuantity = normalizeNumber(
|
|
134
|
+
uomFields.normalizedQuantity,
|
|
135
|
+
quantity
|
|
136
|
+
);
|
|
137
|
+
const normalizedUnit = canonicalizeUnitCode(uomFields.normalizedUnit) ?? quantityUnit;
|
|
138
|
+
const uomSnapshot = uomFields.uomSnapshot;
|
|
139
|
+
const unitPriceNetRaw = normalizeNumber(
|
|
140
|
+
item.unit_price_net ?? item.unitPriceNet,
|
|
141
|
+
Number.NaN
|
|
142
|
+
);
|
|
143
|
+
const unitPriceGrossRaw = normalizeNumber(
|
|
144
|
+
item.unit_price_gross ?? item.unitPriceGross,
|
|
145
|
+
Number.NaN
|
|
146
|
+
);
|
|
147
|
+
const unitPriceNet = Number.isFinite(unitPriceNetRaw) ? unitPriceNetRaw : Number.isFinite(unitPriceGrossRaw) ? unitPriceGrossRaw / (1 + taxRate / 100) : 0;
|
|
148
|
+
const unitPriceGross = Number.isFinite(unitPriceGrossRaw) ? unitPriceGrossRaw : Number.isFinite(unitPriceNetRaw) ? unitPriceNetRaw * (1 + taxRate / 100) : 0;
|
|
149
|
+
const totalNetRaw = normalizeNumber(
|
|
150
|
+
item.total_net_amount ?? item.totalNetAmount,
|
|
151
|
+
Number.NaN
|
|
152
|
+
);
|
|
153
|
+
const totalGrossRaw = normalizeNumber(
|
|
154
|
+
item.total_gross_amount ?? item.totalGrossAmount,
|
|
155
|
+
Number.NaN
|
|
156
|
+
);
|
|
157
|
+
const totalNet = Number.isFinite(totalNetRaw) ? totalNetRaw : unitPriceNet * quantity;
|
|
158
|
+
const totalGross = Number.isFinite(totalGrossRaw) ? totalGrossRaw : unitPriceGross * quantity;
|
|
159
|
+
const priceModeRaw = item.metadata && typeof item.metadata === "object" ? item.metadata.priceMode : null;
|
|
160
|
+
const priceMode = priceModeRaw === "net" ? "net" : "gross";
|
|
161
|
+
const customFieldSetId = typeof item.custom_field_set_id === "string" ? item.custom_field_set_id : typeof item.customFieldSetId === "string" ? item.customFieldSetId : null;
|
|
162
|
+
const statusEntryId = typeof item.status_entry_id === "string" ? item.status_entry_id : typeof item.statusEntryId === "string" ? item.statusEntryId : null;
|
|
163
|
+
const status = typeof item.status === "string" ? item.status : null;
|
|
164
|
+
const record = {
|
|
165
|
+
id,
|
|
166
|
+
name,
|
|
167
|
+
productId: typeof item.product_id === "string" ? item.product_id : null,
|
|
168
|
+
productVariantId: typeof item.product_variant_id === "string" ? item.product_variant_id : null,
|
|
169
|
+
quantity,
|
|
170
|
+
quantityUnit,
|
|
171
|
+
normalizedQuantity,
|
|
172
|
+
normalizedUnit,
|
|
173
|
+
currencyCode: typeof item.currency_code === "string" ? item.currency_code : typeof currencyCode === "string" ? currencyCode : null,
|
|
174
|
+
unitPriceNet,
|
|
175
|
+
unitPriceGross,
|
|
176
|
+
taxRate,
|
|
177
|
+
totalNet,
|
|
178
|
+
totalGross,
|
|
179
|
+
priceMode,
|
|
180
|
+
uomSnapshot,
|
|
181
|
+
metadata: item.metadata ?? null,
|
|
182
|
+
catalogSnapshot: item.catalog_snapshot ?? null,
|
|
183
|
+
customFieldSetId,
|
|
184
|
+
customFields: Object.keys(customFields).length ? customFields : null,
|
|
185
|
+
status,
|
|
186
|
+
statusEntryId
|
|
187
|
+
};
|
|
188
|
+
return [record];
|
|
189
|
+
}
|
|
190
|
+
);
|
|
129
191
|
setItems(mapped);
|
|
130
192
|
if (onItemsChange) onItemsChange(mapped);
|
|
131
193
|
} else {
|
|
@@ -146,12 +208,14 @@ function SalesDocumentItemsSection({
|
|
|
146
208
|
return;
|
|
147
209
|
}
|
|
148
210
|
try {
|
|
149
|
-
const params = new URLSearchParams({
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
)
|
|
211
|
+
const params = new URLSearchParams({
|
|
212
|
+
page: "1",
|
|
213
|
+
pageSize: "100",
|
|
214
|
+
orderId: documentId
|
|
215
|
+
});
|
|
216
|
+
const response = await apiCall(`/api/sales/shipments?${params.toString()}`, void 0, {
|
|
217
|
+
fallback: { items: [] }
|
|
218
|
+
});
|
|
155
219
|
if (response.ok && Array.isArray(response.result?.items)) {
|
|
156
220
|
const totals = /* @__PURE__ */ new Map();
|
|
157
221
|
response.result.items.forEach((shipment) => {
|
|
@@ -228,7 +292,10 @@ function SalesDocumentItemsSection({
|
|
|
228
292
|
const handleDelete = React.useCallback(
|
|
229
293
|
async (line) => {
|
|
230
294
|
const confirmed = await confirm({
|
|
231
|
-
title: t(
|
|
295
|
+
title: t(
|
|
296
|
+
"sales.documents.items.deleteConfirm",
|
|
297
|
+
"Delete this line item?"
|
|
298
|
+
),
|
|
232
299
|
variant: "destructive"
|
|
233
300
|
});
|
|
234
301
|
if (!confirmed) return;
|
|
@@ -240,7 +307,10 @@ function SalesDocumentItemsSection({
|
|
|
240
307
|
organizationId: resolvedOrganizationId ?? void 0,
|
|
241
308
|
tenantId: resolvedTenantId ?? void 0
|
|
242
309
|
},
|
|
243
|
-
errorMessage: t(
|
|
310
|
+
errorMessage: t(
|
|
311
|
+
"sales.documents.items.errorDelete",
|
|
312
|
+
"Failed to delete line."
|
|
313
|
+
)
|
|
244
314
|
});
|
|
245
315
|
if (result.ok) {
|
|
246
316
|
flash(t("sales.documents.items.deleted", "Line removed."), "success");
|
|
@@ -250,11 +320,24 @@ function SalesDocumentItemsSection({
|
|
|
250
320
|
} catch (err) {
|
|
251
321
|
console.error("sales.document.items.delete", err);
|
|
252
322
|
const normalized = normalizeCrudServerError(err);
|
|
253
|
-
const fallback = t(
|
|
323
|
+
const fallback = t(
|
|
324
|
+
"sales.documents.items.errorDelete",
|
|
325
|
+
"Failed to delete line."
|
|
326
|
+
);
|
|
254
327
|
flash(normalized.message || fallback, "error");
|
|
255
328
|
}
|
|
256
329
|
},
|
|
257
|
-
[
|
|
330
|
+
[
|
|
331
|
+
confirm,
|
|
332
|
+
documentId,
|
|
333
|
+
documentKey,
|
|
334
|
+
kind,
|
|
335
|
+
loadItems,
|
|
336
|
+
resolvedOrganizationId,
|
|
337
|
+
resourcePath,
|
|
338
|
+
t,
|
|
339
|
+
resolvedTenantId
|
|
340
|
+
]
|
|
258
341
|
);
|
|
259
342
|
const renderStatus = React.useCallback(
|
|
260
343
|
(line) => {
|
|
@@ -286,7 +369,14 @@ function SalesDocumentItemsSection({
|
|
|
286
369
|
const variantThumb = meta && typeof meta.variantThumbnail === "string" && meta.variantThumbnail || variantSnapshot && typeof variantSnapshot.thumbnailUrl === "string" && variantSnapshot.thumbnailUrl || null;
|
|
287
370
|
const thumbnail = variantThumb ?? productThumb;
|
|
288
371
|
if (thumbnail) {
|
|
289
|
-
return /* @__PURE__ */ jsx(
|
|
372
|
+
return /* @__PURE__ */ jsx(
|
|
373
|
+
"img",
|
|
374
|
+
{
|
|
375
|
+
src: thumbnail,
|
|
376
|
+
alt: record.name ?? record.id,
|
|
377
|
+
className: "h-10 w-10 rounded border object-cover"
|
|
378
|
+
}
|
|
379
|
+
);
|
|
290
380
|
}
|
|
291
381
|
return /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded border bg-muted text-xs text-muted-foreground", children: "N/A" });
|
|
292
382
|
};
|
|
@@ -326,7 +416,15 @@ function SalesDocumentItemsSection({
|
|
|
326
416
|
const variantLabel = variantTitle ?? variantSku;
|
|
327
417
|
const variantSuffix = variantSku && variantLabel && variantSku !== variantLabel ? ` \u2022 ${variantSku}` : "";
|
|
328
418
|
const showProductSku = productSku && productSku !== variantSku ? productSku : null;
|
|
329
|
-
const shippedQuantity = Math.max(
|
|
419
|
+
const shippedQuantity = Math.max(
|
|
420
|
+
0,
|
|
421
|
+
shippedTotals.get(item.id) ?? 0
|
|
422
|
+
);
|
|
423
|
+
const quantityLabel = item.quantityUnit ? `${item.quantity} ${item.quantityUnit}` : String(item.quantity);
|
|
424
|
+
const showNormalized = Number.isFinite(item.normalizedQuantity) && item.normalizedQuantity > 0 && (item.normalizedUnit ?? null) && (Math.abs(item.normalizedQuantity - item.quantity) > 1e-6 || (item.normalizedUnit ?? null) !== (item.quantityUnit ?? null));
|
|
425
|
+
const unitPriceReference = resolveUnitPriceReference(
|
|
426
|
+
item.uomSnapshot
|
|
427
|
+
);
|
|
330
428
|
return /* @__PURE__ */ jsxs(
|
|
331
429
|
"tr",
|
|
332
430
|
{
|
|
@@ -346,32 +444,64 @@ function SalesDocumentItemsSection({
|
|
|
346
444
|
] }) }),
|
|
347
445
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsx("div", { className: "flex items-center", children: renderStatus(item) }) }),
|
|
348
446
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
349
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children:
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
447
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: quantityLabel }),
|
|
448
|
+
showNormalized ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
449
|
+
item.normalizedQuantity,
|
|
450
|
+
" ",
|
|
451
|
+
item.normalizedUnit
|
|
452
|
+
] }) : null,
|
|
453
|
+
shippedQuantity > 0 ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t(
|
|
454
|
+
"sales.documents.items.table.shipped",
|
|
455
|
+
"{{shipped}} / {{total}} shipped",
|
|
456
|
+
{
|
|
457
|
+
shipped: shippedQuantity,
|
|
458
|
+
total: item.quantity
|
|
459
|
+
}
|
|
460
|
+
) }) : null
|
|
354
461
|
] }) }),
|
|
355
462
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
356
463
|
/* @__PURE__ */ jsxs("span", { className: "font-mono text-sm", children: [
|
|
357
|
-
formatMoney(
|
|
464
|
+
formatMoney(
|
|
465
|
+
item.unitPriceGross,
|
|
466
|
+
item.currencyCode ?? currencyCode ?? void 0
|
|
467
|
+
),
|
|
358
468
|
" ",
|
|
359
469
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t("sales.documents.items.table.gross", "gross") })
|
|
360
470
|
] }),
|
|
361
471
|
/* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [
|
|
362
|
-
formatMoney(
|
|
472
|
+
formatMoney(
|
|
473
|
+
item.unitPriceNet,
|
|
474
|
+
item.currencyCode ?? currencyCode ?? void 0
|
|
475
|
+
),
|
|
363
476
|
" ",
|
|
364
477
|
t("sales.documents.items.table.net", "net")
|
|
365
|
-
] })
|
|
478
|
+
] }),
|
|
479
|
+
unitPriceReference ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t(
|
|
480
|
+
"sales.documents.items.table.unitPriceReference",
|
|
481
|
+
"{{value}} per 1 {{unit}}",
|
|
482
|
+
{
|
|
483
|
+
value: formatMoney(
|
|
484
|
+
unitPriceReference.grossPerReference,
|
|
485
|
+
item.currencyCode ?? currencyCode ?? void 0
|
|
486
|
+
),
|
|
487
|
+
unit: unitPriceReference.referenceUnitCode
|
|
488
|
+
}
|
|
489
|
+
) }) : null
|
|
366
490
|
] }) }),
|
|
367
491
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-3 font-semibold", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
368
492
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
369
|
-
formatMoney(
|
|
493
|
+
formatMoney(
|
|
494
|
+
item.totalGross,
|
|
495
|
+
item.currencyCode ?? currencyCode ?? void 0
|
|
496
|
+
),
|
|
370
497
|
" ",
|
|
371
498
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-muted-foreground", children: t("sales.documents.items.table.gross", "gross") })
|
|
372
499
|
] }),
|
|
373
500
|
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground", children: [
|
|
374
|
-
formatMoney(
|
|
501
|
+
formatMoney(
|
|
502
|
+
item.totalNet,
|
|
503
|
+
item.currencyCode ?? currencyCode ?? void 0
|
|
504
|
+
),
|
|
375
505
|
" ",
|
|
376
506
|
t("sales.documents.items.table.net", "net")
|
|
377
507
|
] })
|