@open-mercato/core 0.6.8-develop.7088.1.3873d81f42 → 0.6.8-develop.7091.1.15ffbe30ce
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/modules/customers/components/DealsKpiStrip.js +7 -6
- package/dist/modules/customers/components/DealsKpiStrip.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js +5 -4
- package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +6 -5
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/utils.js +5 -5
- package/dist/modules/customers/components/detail/utils.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +10 -8
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/components/PriceWithCurrency.js +7 -5
- package/dist/modules/sales/components/PriceWithCurrency.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +14 -7
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +8 -7
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +9 -8
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnsSection.js +8 -6
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +5 -4
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +5 -4
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/ShipmentsSection.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/auth/i18n/de.json +3 -0
- package/src/modules/auth/i18n/en.json +3 -0
- package/src/modules/auth/i18n/es.json +3 -0
- package/src/modules/auth/i18n/ko.json +3 -0
- package/src/modules/auth/i18n/pl.json +3 -0
- package/src/modules/customers/components/DealsKpiStrip.tsx +7 -7
- package/src/modules/customers/components/detail/CompanyKpiBar.tsx +5 -4
- package/src/modules/customers/components/detail/DealsSection.tsx +11 -5
- package/src/modules/customers/components/detail/utils.ts +15 -5
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +14 -8
- package/src/modules/sales/components/PriceWithCurrency.tsx +17 -5
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/ItemsSection.tsx +8 -1
- package/src/modules/sales/components/documents/LineItemDialog.tsx +9 -8
- package/src/modules/sales/components/documents/PaymentsSection.tsx +9 -8
- package/src/modules/sales/components/documents/ReturnsSection.tsx +6 -5
- package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +5 -4
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +5 -4
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/lineItemUtils.ts +7 -2
|
@@ -23,7 +23,7 @@ import type { SectionAction } from '@open-mercato/core/modules/customers/compone
|
|
|
23
23
|
import type { SalesAdjustmentKind } from '../../data/entities'
|
|
24
24
|
import { PriceWithCurrency } from '../PriceWithCurrency'
|
|
25
25
|
import { AdjustmentDialog, type AdjustmentRowData, type AdjustmentSubmitPayload } from './AdjustmentDialog'
|
|
26
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
26
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
27
27
|
import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
28
28
|
import { extractCustomFieldValues } from './customFieldHelpers'
|
|
29
29
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
@@ -60,9 +60,9 @@ function normalizeNumber(value: unknown, fallback = 0): number {
|
|
|
60
60
|
return fallback
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function formatPercent(value: number | null | undefined): string {
|
|
63
|
+
export function formatPercent(value: number | null | undefined, locale?: string): string {
|
|
64
64
|
if (value === null || value === undefined) return '—'
|
|
65
|
-
const formatter = new Intl.NumberFormat(
|
|
65
|
+
const formatter = new Intl.NumberFormat(locale, { style: 'percent', maximumFractionDigits: 2 })
|
|
66
66
|
return formatter.format(value / 100)
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -77,6 +77,7 @@ export function SalesDocumentAdjustmentsSection({
|
|
|
77
77
|
onRowsChange,
|
|
78
78
|
}: SalesDocumentAdjustmentsSectionProps) {
|
|
79
79
|
const t = useT()
|
|
80
|
+
const locale = useLocale()
|
|
80
81
|
const { organizationId, tenantId } = useOrganizationScopeDetail()
|
|
81
82
|
const resolvedOrganizationId = orgFromProps ?? organizationId ?? null
|
|
82
83
|
const resolvedTenantId = tenantFromProps ?? tenantId ?? null
|
|
@@ -433,7 +434,7 @@ export function SalesDocumentAdjustmentsSection({
|
|
|
433
434
|
{
|
|
434
435
|
id: 'rate',
|
|
435
436
|
header: t('sales.documents.adjustments.rate', 'Rate'),
|
|
436
|
-
cell: ({ row }) => <span className="font-mono text-sm text-muted-foreground">{formatPercent(row.original.rate)}</span>,
|
|
437
|
+
cell: ({ row }) => <span className="font-mono text-sm text-muted-foreground">{formatPercent(row.original.rate, locale)}</span>,
|
|
437
438
|
},
|
|
438
439
|
{
|
|
439
440
|
id: 'position',
|
|
@@ -441,7 +442,7 @@ export function SalesDocumentAdjustmentsSection({
|
|
|
441
442
|
cell: ({ row }) => <span className="text-sm text-muted-foreground">{row.original.position}</span>,
|
|
442
443
|
},
|
|
443
444
|
],
|
|
444
|
-
[currencyCode, resolveKindLabel, t]
|
|
445
|
+
[currencyCode, locale, resolveKindLabel, t]
|
|
445
446
|
)
|
|
446
447
|
|
|
447
448
|
const showLoadingState = loading && rows.length === 0
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
createDictionaryMap,
|
|
17
17
|
normalizeDictionaryEntries,
|
|
18
18
|
} from "@open-mercato/core/modules/dictionaries/components/dictionaryAppearance";
|
|
19
|
-
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
19
|
+
import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
|
|
20
20
|
import { useOrganizationScopeDetail } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
21
21
|
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
22
22
|
import { emitSalesDocumentTotalsRefresh } from "@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents";
|
|
@@ -158,6 +158,7 @@ export function SalesDocumentItemsSection({
|
|
|
158
158
|
onItemsChange,
|
|
159
159
|
}: SalesDocumentItemsSectionProps) {
|
|
160
160
|
const t = useT();
|
|
161
|
+
const locale = useLocale();
|
|
161
162
|
const { organizationId, tenantId } = useOrganizationScopeDetail();
|
|
162
163
|
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
163
164
|
const resolvedOrganizationId = orgFromProps ?? organizationId ?? null;
|
|
@@ -827,6 +828,7 @@ export function SalesDocumentItemsSection({
|
|
|
827
828
|
{formatMoney(
|
|
828
829
|
item.unitPriceGross,
|
|
829
830
|
item.currencyCode ?? currencyCode ?? undefined,
|
|
831
|
+
locale,
|
|
830
832
|
)}{" "}
|
|
831
833
|
<span className="text-xs text-muted-foreground">
|
|
832
834
|
{t("sales.documents.items.table.gross", "gross")}
|
|
@@ -836,6 +838,7 @@ export function SalesDocumentItemsSection({
|
|
|
836
838
|
{formatMoney(
|
|
837
839
|
item.unitPriceNet,
|
|
838
840
|
item.currencyCode ?? currencyCode ?? undefined,
|
|
841
|
+
locale,
|
|
839
842
|
)}{" "}
|
|
840
843
|
{t("sales.documents.items.table.net", "net")}
|
|
841
844
|
</span>
|
|
@@ -850,6 +853,7 @@ export function SalesDocumentItemsSection({
|
|
|
850
853
|
item.currencyCode ??
|
|
851
854
|
currencyCode ??
|
|
852
855
|
undefined,
|
|
856
|
+
locale,
|
|
853
857
|
),
|
|
854
858
|
unit: unitPriceReference.referenceUnitCode,
|
|
855
859
|
},
|
|
@@ -873,6 +877,7 @@ export function SalesDocumentItemsSection({
|
|
|
873
877
|
item.currencyCode ??
|
|
874
878
|
currencyCode ??
|
|
875
879
|
undefined,
|
|
880
|
+
locale,
|
|
876
881
|
),
|
|
877
882
|
},
|
|
878
883
|
)}
|
|
@@ -903,6 +908,7 @@ export function SalesDocumentItemsSection({
|
|
|
903
908
|
{formatMoney(
|
|
904
909
|
item.totalGross,
|
|
905
910
|
item.currencyCode ?? currencyCode ?? undefined,
|
|
911
|
+
locale,
|
|
906
912
|
)}{" "}
|
|
907
913
|
<span className="text-xs font-normal text-muted-foreground">
|
|
908
914
|
{t("sales.documents.items.table.gross", "gross")}
|
|
@@ -912,6 +918,7 @@ export function SalesDocumentItemsSection({
|
|
|
912
918
|
{formatMoney(
|
|
913
919
|
item.totalNet,
|
|
914
920
|
item.currencyCode ?? currencyCode ?? undefined,
|
|
921
|
+
locale,
|
|
915
922
|
)}{" "}
|
|
916
923
|
{t("sales.documents.items.table.net", "net")}
|
|
917
924
|
</span>
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
renderDictionaryColor,
|
|
47
47
|
} from "@open-mercato/core/modules/dictionaries/components/dictionaryAppearance";
|
|
48
48
|
import { E } from "#generated/entities.ids.generated";
|
|
49
|
-
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
49
|
+
import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
|
|
50
50
|
import { useOrganizationScopeDetail } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
51
51
|
import { formatMoney, normalizeNumber } from "./lineItemUtils";
|
|
52
52
|
import type { SalesLineRecord } from "./lineItemTypes";
|
|
@@ -489,6 +489,7 @@ export function LineItemDialog({
|
|
|
489
489
|
onDraftSaved,
|
|
490
490
|
}: SalesLineDialogProps) {
|
|
491
491
|
const t = useT();
|
|
492
|
+
const locale = useLocale();
|
|
492
493
|
const scope = useOrganizationScopeDetail();
|
|
493
494
|
const resolvedOrganizationId = organizationId ?? scope.organizationId ?? null;
|
|
494
495
|
const resolvedTenantId = tenantId ?? scope.tenantId ?? null;
|
|
@@ -1044,10 +1045,10 @@ export function LineItemDialog({
|
|
|
1044
1045
|
displayMode === "including-tax" &&
|
|
1045
1046
|
amountGross !== null &&
|
|
1046
1047
|
currency
|
|
1047
|
-
? formatMoney(amountGross, currency)
|
|
1048
|
+
? formatMoney(amountGross, currency, locale)
|
|
1048
1049
|
: null,
|
|
1049
1050
|
displayMode === "excluding-tax" && amountNet !== null && currency
|
|
1050
|
-
? formatMoney(amountNet, currency)
|
|
1051
|
+
? formatMoney(amountNet, currency, locale)
|
|
1051
1052
|
: null,
|
|
1052
1053
|
displayMode
|
|
1053
1054
|
? displayMode === "including-tax"
|
|
@@ -1063,9 +1064,9 @@ export function LineItemDialog({
|
|
|
1063
1064
|
labelParts.length > 0
|
|
1064
1065
|
? labelParts.join(" • ")
|
|
1065
1066
|
: amountGross !== null && currency
|
|
1066
|
-
? formatMoney(amountGross, currency)
|
|
1067
|
+
? formatMoney(amountGross, currency, locale)
|
|
1067
1068
|
: amountNet !== null && currency
|
|
1068
|
-
? formatMoney(amountNet, currency)
|
|
1069
|
+
? formatMoney(amountNet, currency, locale)
|
|
1069
1070
|
: id;
|
|
1070
1071
|
return {
|
|
1071
1072
|
id,
|
|
@@ -2059,7 +2060,7 @@ export function LineItemDialog({
|
|
|
2059
2060
|
? t("sales.documents.items.priceNet", "Net")
|
|
2060
2061
|
: t("sales.documents.items.priceGross", "Gross");
|
|
2061
2062
|
const lockedAmountLabel = Number.isFinite(lockedAmount)
|
|
2062
|
-
? `${formatMoney(lockedAmount, lockedCurrency)} — ${lockedModeLabel}`
|
|
2063
|
+
? `${formatMoney(lockedAmount, lockedCurrency, locale)} — ${lockedModeLabel}`
|
|
2063
2064
|
: lockedModeLabel;
|
|
2064
2065
|
const lockedPriceDetail =
|
|
2065
2066
|
selectedPrice?.priceKindTitle ??
|
|
@@ -2260,11 +2261,11 @@ export function LineItemDialog({
|
|
|
2260
2261
|
"sales.documents.items.priceBasisTemplate",
|
|
2261
2262
|
"Catalog price basis: {{baseAmount}} / {{baseUnit}}. Converted for {{unit}}: {{baseAmount}} × {{factor}} = {{convertedAmount}}.",
|
|
2262
2263
|
{
|
|
2263
|
-
baseAmount: formatMoney(selectedBaseAmount as number, selectedCurrency),
|
|
2264
|
+
baseAmount: formatMoney(selectedBaseAmount as number, selectedCurrency, locale),
|
|
2264
2265
|
baseUnit: baseUnitCode,
|
|
2265
2266
|
unit: quantityUnitCode,
|
|
2266
2267
|
factor: unitFactor,
|
|
2267
|
-
convertedAmount: formatMoney(convertedAmount, selectedCurrency),
|
|
2268
|
+
convertedAmount: formatMoney(convertedAmount, selectedCurrency, locale),
|
|
2268
2269
|
},
|
|
2269
2270
|
)}
|
|
2270
2271
|
</p>
|
|
@@ -13,7 +13,7 @@ import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
|
13
13
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
14
14
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
15
15
|
import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
16
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
16
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
17
17
|
import { emitSalesDocumentTotalsRefresh } from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'
|
|
18
18
|
import { extensionPoints } from '@open-mercato/core/modules/sales/extension-points'
|
|
19
19
|
import { PaymentDialog, type PaymentFormData, type PaymentTotals } from './PaymentDialog'
|
|
@@ -60,10 +60,10 @@ function normalizeNumber(value: unknown): number {
|
|
|
60
60
|
return 0
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function formatMoney(value: number, currency: string | null | undefined): string {
|
|
63
|
+
function formatMoney(value: number, currency: string | null | undefined, locale?: string): string {
|
|
64
64
|
if (!currency || currency.trim().length !== 3) return value.toFixed(2)
|
|
65
65
|
try {
|
|
66
|
-
return new Intl.NumberFormat(
|
|
66
|
+
return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value)
|
|
67
67
|
} catch {
|
|
68
68
|
return `${currency.toUpperCase()} ${value.toFixed(2)}`
|
|
69
69
|
}
|
|
@@ -80,6 +80,7 @@ export function SalesDocumentPaymentsSection({
|
|
|
80
80
|
onPaymentsChange,
|
|
81
81
|
}: SalesDocumentPaymentsSectionProps) {
|
|
82
82
|
const t = useT()
|
|
83
|
+
const locale = useLocale()
|
|
83
84
|
const { organizationId, tenantId } = useOrganizationScopeDetail()
|
|
84
85
|
const resolvedOrganizationId = orgFromProps ?? organizationId ?? null
|
|
85
86
|
const resolvedTenantId = tenantFromProps ?? tenantId ?? null
|
|
@@ -287,24 +288,24 @@ export function SalesDocumentPaymentsSection({
|
|
|
287
288
|
{
|
|
288
289
|
accessorKey: 'amount',
|
|
289
290
|
header: t('sales.documents.payments.amount', 'Amount'),
|
|
290
|
-
cell: ({ row }) => formatMoney(row.original.amount, row.original.currencyCode ?? currencyCode),
|
|
291
|
+
cell: ({ row }) => formatMoney(row.original.amount, row.original.currencyCode ?? currencyCode, locale),
|
|
291
292
|
},
|
|
292
293
|
{
|
|
293
294
|
accessorKey: 'receivedAt',
|
|
294
295
|
header: t('sales.documents.payments.receivedAt', 'Received'),
|
|
295
296
|
cell: ({ row }) =>
|
|
296
297
|
row.original.receivedAt
|
|
297
|
-
? new Date(row.original.receivedAt).toLocaleDateString()
|
|
298
|
+
? new Date(row.original.receivedAt).toLocaleDateString(locale)
|
|
298
299
|
: '—',
|
|
299
300
|
},
|
|
300
301
|
{
|
|
301
302
|
accessorKey: 'createdAt',
|
|
302
303
|
header: t('sales.documents.payments.createdAt', 'Created'),
|
|
303
304
|
cell: ({ row }) =>
|
|
304
|
-
row.original.createdAt ? new Date(row.original.createdAt).toLocaleString() : '—',
|
|
305
|
+
row.original.createdAt ? new Date(row.original.createdAt).toLocaleString(locale) : '—',
|
|
305
306
|
meta: {
|
|
306
307
|
tooltipContent: (row: PaymentRow) =>
|
|
307
|
-
row.createdAt ? new Date(row.createdAt).toLocaleString() : undefined,
|
|
308
|
+
row.createdAt ? new Date(row.createdAt).toLocaleString(locale) : undefined,
|
|
308
309
|
},
|
|
309
310
|
},
|
|
310
311
|
{
|
|
@@ -327,7 +328,7 @@ export function SalesDocumentPaymentsSection({
|
|
|
327
328
|
},
|
|
328
329
|
},
|
|
329
330
|
],
|
|
330
|
-
[currencyCode, deleteActionLabel, editActionLabel, handleDelete, openEditPayment, t]
|
|
331
|
+
[currencyCode, deleteActionLabel, editActionLabel, handleDelete, locale, openEditPayment, t]
|
|
331
332
|
)
|
|
332
333
|
|
|
333
334
|
if (loading) {
|
|
@@ -12,7 +12,7 @@ import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
|
12
12
|
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
13
13
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
14
14
|
import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
15
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
15
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
16
16
|
import {
|
|
17
17
|
emitSalesDocumentTotalsRefresh,
|
|
18
18
|
subscribeSalesDocumentTotalsRefresh,
|
|
@@ -41,15 +41,16 @@ type SalesReturnsSectionProps = {
|
|
|
41
41
|
documentUpdatedAt?: string | null
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function formatDisplayDate(value: string | null | undefined): string | null {
|
|
44
|
+
export function formatDisplayDate(value: string | null | undefined, locale?: string): string | null {
|
|
45
45
|
if (!value) return null
|
|
46
46
|
const date = new Date(value)
|
|
47
47
|
if (Number.isNaN(date.getTime())) return null
|
|
48
|
-
return new Intl.DateTimeFormat(
|
|
48
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }: SalesReturnsSectionProps) {
|
|
52
52
|
const t = useT()
|
|
53
|
+
const locale = useLocale()
|
|
53
54
|
const { organizationId, tenantId } = useOrganizationScopeDetail()
|
|
54
55
|
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
55
56
|
const [returns, setReturns] = React.useState<ReturnRow[]>([])
|
|
@@ -328,10 +329,10 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
|
|
|
328
329
|
</div>
|
|
329
330
|
</div>
|
|
330
331
|
<div className="whitespace-nowrap text-right text-sm text-muted-foreground">
|
|
331
|
-
{formatDisplayDate(ret.returnedAt) ?? t('sales.returns.notSet', 'Not set')}
|
|
332
|
+
{formatDisplayDate(ret.returnedAt, locale) ?? t('sales.returns.notSet', 'Not set')}
|
|
332
333
|
</div>
|
|
333
334
|
<div className="whitespace-nowrap text-right text-sm font-medium">
|
|
334
|
-
{formatMoney(ret.total, currencyCode ?? null)}
|
|
335
|
+
{formatMoney(ret.total, currencyCode ?? null, locale)}
|
|
335
336
|
</div>
|
|
336
337
|
<div className="flex justify-end">
|
|
337
338
|
<RowActions
|
|
@@ -7,7 +7,7 @@ import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
|
7
7
|
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
8
8
|
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
9
9
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
10
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
10
|
+
import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
|
|
11
11
|
import { LineItemDialog } from './LineItemDialog'
|
|
12
12
|
import type { SalesLineRecord } from './lineItemTypes'
|
|
13
13
|
import { formatMoney, normalizeNumber } from './lineItemUtils'
|
|
@@ -93,6 +93,7 @@ export function SalesOrderDraftLines({
|
|
|
93
93
|
onChange,
|
|
94
94
|
}: SalesOrderDraftLinesProps) {
|
|
95
95
|
const t = useT()
|
|
96
|
+
const locale = useLocale()
|
|
96
97
|
const [dialogOpen, setDialogOpen] = React.useState(false)
|
|
97
98
|
const [editing, setEditing] = React.useState<SalesOrderLineDraft | null>(null)
|
|
98
99
|
|
|
@@ -112,14 +113,14 @@ export function SalesOrderDraftLines({
|
|
|
112
113
|
{
|
|
113
114
|
id: 'unitPrice',
|
|
114
115
|
header: t('sales.documents.items.table.unit', 'Unit price'),
|
|
115
|
-
cell: ({ row }) => formatMoney(row.original.record.unitPriceGross, row.original.record.currencyCode ?? currencyCode ?? undefined),
|
|
116
|
+
cell: ({ row }) => formatMoney(row.original.record.unitPriceGross, row.original.record.currencyCode ?? currencyCode ?? undefined, locale),
|
|
116
117
|
},
|
|
117
118
|
{
|
|
118
119
|
id: 'total',
|
|
119
120
|
header: t('sales.documents.items.table.total', 'Total'),
|
|
120
|
-
cell: ({ row }) => formatMoney(row.original.record.totalGross, row.original.record.currencyCode ?? currencyCode ?? undefined),
|
|
121
|
+
cell: ({ row }) => formatMoney(row.original.record.totalGross, row.original.record.currencyCode ?? currencyCode ?? undefined, locale),
|
|
121
122
|
},
|
|
122
|
-
], [currencyCode, t])
|
|
123
|
+
], [currencyCode, locale, t])
|
|
123
124
|
|
|
124
125
|
const openCreate = React.useCallback(() => {
|
|
125
126
|
setEditing(null)
|
|
@@ -20,7 +20,7 @@ import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
|
20
20
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
21
21
|
import { E } from '#generated/entities.ids.generated'
|
|
22
22
|
import { emitSalesDocumentTotalsRefresh } from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'
|
|
23
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
23
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
24
24
|
import { formatMoney, normalizeNumber } from './lineItemUtils'
|
|
25
25
|
import type { OrderLine, ShipmentRow } from './shipmentTypes'
|
|
26
26
|
import { formatAddressString, type AddressFormatStrategy, type AddressValue } from '@open-mercato/core/modules/customers/utils/addressFormat'
|
|
@@ -290,6 +290,7 @@ export function ShipmentDialog({
|
|
|
290
290
|
onAddComment,
|
|
291
291
|
}: ShipmentDialogProps) {
|
|
292
292
|
const t = useT()
|
|
293
|
+
const locale = useLocale()
|
|
293
294
|
const [formResetKey, setFormResetKey] = React.useState(0)
|
|
294
295
|
const [shippingMethods, setShippingMethods] = React.useState<ShippingMethodOption[]>([])
|
|
295
296
|
const [shippingMethodLoading, setShippingMethodLoading] = React.useState(false)
|
|
@@ -540,21 +541,21 @@ export function ShipmentDialog({
|
|
|
540
541
|
if (option.avgPrice !== null) {
|
|
541
542
|
parts.push(
|
|
542
543
|
t('sales.documents.shipments.shippingMethodAvg', 'Avg {{price}}', {
|
|
543
|
-
price: formatMoney(option.avgPrice, currency ?? null),
|
|
544
|
+
price: formatMoney(option.avgPrice, currency ?? null, locale),
|
|
544
545
|
}),
|
|
545
546
|
)
|
|
546
547
|
}
|
|
547
548
|
if (option.minPrice !== null) {
|
|
548
549
|
parts.push(
|
|
549
550
|
t('sales.documents.shipments.shippingMethodMin', 'Min {{price}}', {
|
|
550
|
-
price: formatMoney(option.minPrice, currency ?? null),
|
|
551
|
+
price: formatMoney(option.minPrice, currency ?? null, locale),
|
|
551
552
|
}),
|
|
552
553
|
)
|
|
553
554
|
}
|
|
554
555
|
if (parts.length === 0) return undefined
|
|
555
556
|
return parts.join(' · ')
|
|
556
557
|
},
|
|
557
|
-
[currencyCode, t],
|
|
558
|
+
[currencyCode, locale, t],
|
|
558
559
|
)
|
|
559
560
|
|
|
560
561
|
const mapShippingMethod = React.useCallback(
|
|
@@ -10,7 +10,7 @@ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/u
|
|
|
10
10
|
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
11
11
|
import { deleteCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
12
12
|
import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
13
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
13
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
14
14
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
15
15
|
import {
|
|
16
16
|
emitSalesDocumentTotalsRefresh,
|
|
@@ -41,11 +41,11 @@ type SalesShipmentsSectionProps = {
|
|
|
41
41
|
onAddComment?: (body: string) => Promise<void>
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function formatDisplayDate(value: string | null | undefined): string | null {
|
|
44
|
+
export function formatDisplayDate(value: string | null | undefined, locale?: string): string | null {
|
|
45
45
|
if (!value) return null
|
|
46
46
|
const date = new Date(value)
|
|
47
47
|
if (Number.isNaN(date.getTime())) return null
|
|
48
|
-
return new Intl.DateTimeFormat(
|
|
48
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const formatShipmentAddress = (metadata?: Record<string, unknown> | null): string | null => {
|
|
@@ -123,6 +123,7 @@ export function SalesShipmentsSection({
|
|
|
123
123
|
onAddComment,
|
|
124
124
|
}: SalesShipmentsSectionProps) {
|
|
125
125
|
const t = useT()
|
|
126
|
+
const locale = useLocale()
|
|
126
127
|
const { organizationId, tenantId } = useOrganizationScopeDetail()
|
|
127
128
|
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
128
129
|
const resolvedOrganizationId = organizationIdProp ?? organizationId ?? null
|
|
@@ -486,8 +487,8 @@ export function SalesShipmentsSection({
|
|
|
486
487
|
) : (
|
|
487
488
|
<div className="grid gap-3 md:grid-cols-2">
|
|
488
489
|
{shipments.map((shipment) => {
|
|
489
|
-
const shippedAt = formatDisplayDate(shipment.shippedAt)
|
|
490
|
-
const deliveredAt = formatDisplayDate(shipment.deliveredAt)
|
|
490
|
+
const shippedAt = formatDisplayDate(shipment.shippedAt, locale)
|
|
491
|
+
const deliveredAt = formatDisplayDate(shipment.deliveredAt, locale)
|
|
491
492
|
const addressSummary = formatShipmentAddress(shipment.metadata)
|
|
492
493
|
const statusLabel =
|
|
493
494
|
shipment.statusLabel ??
|
|
@@ -9,9 +9,14 @@ export function normalizeNumber(value: unknown, fallback = 0): number {
|
|
|
9
9
|
return fallback
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Pass `locale` to format in the application locale (`useLocale()` in client components).
|
|
14
|
+
* Omitting it keeps the runtime default, which varies per machine and is therefore not
|
|
15
|
+
* assertable in tests.
|
|
16
|
+
*/
|
|
17
|
+
export function formatMoney(value: number, currency: string | null | undefined, locale?: string): string {
|
|
13
18
|
if (!currency) return value.toFixed(2)
|
|
14
|
-
return new Intl.NumberFormat(
|
|
19
|
+
return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value)
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
export type LineDiscountDisplay = {
|