@open-mercato/core 0.7.1-develop.7153.1.7145d295e6 → 0.7.1-develop.7170.1.d95074d7ba
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/AGENTS.md +2 -2
- package/dist/modules/auth/commands/users.js +14 -1
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/entities.js +2 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/lib/backendChrome.js +26 -3
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/auth/lib/profile-sections.js +1 -1
- package/dist/modules/auth/lib/profile-sections.js.map +2 -2
- package/dist/modules/auth/migrations/Migration20260907120000_auth.js +19 -0
- package/dist/modules/auth/migrations/Migration20260907120000_auth.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +2 -2
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +1 -1
- package/dist/modules/customer_accounts/api/admin/demo-accounts.js +118 -0
- package/dist/modules/customer_accounts/api/admin/demo-accounts.js.map +7 -0
- package/dist/modules/customer_accounts/api/invitations/accept.js +23 -6
- package/dist/modules/customer_accounts/api/invitations/accept.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.js +10 -13
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/useDemoPortalAccounts.js +28 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/useDemoPortalAccounts.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +6 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
- package/dist/modules/customer_accounts/data/entities.js +4 -1
- package/dist/modules/customer_accounts/data/entities.js.map +2 -2
- package/dist/modules/customer_accounts/lib/exampleAccounts.js +9 -0
- package/dist/modules/customer_accounts/lib/exampleAccounts.js.map +7 -0
- package/dist/modules/customer_accounts/migrations/Migration20260827160000_customer_accounts.js +16 -0
- package/dist/modules/customer_accounts/migrations/Migration20260827160000_customer_accounts.js.map +7 -0
- package/dist/modules/customer_accounts/services/customerInvitationService.js +53 -6
- package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
- package/dist/modules/customer_accounts/setup.js +2 -6
- package/dist/modules/customer_accounts/setup.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +55 -40
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customers/commands/people.js +16 -1
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +18 -36
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-schedule-service.js +25 -23
- package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
- package/dist/modules/entities/api/records.js +18 -15
- package/dist/modules/entities/api/records.js.map +3 -3
- package/dist/modules/entities/lib/scoped-field-defs.js +52 -0
- package/dist/modules/entities/lib/scoped-field-defs.js.map +7 -0
- package/dist/modules/entities/lib/validation.js +4 -35
- package/dist/modules/entities/lib/validation.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +32 -54
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +12 -29
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js +2 -2
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js.map +1 -1
- package/dist/modules/progress/lib/progressServiceImpl.js +14 -3
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +9 -9
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +50 -10
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +8 -3
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnEditDialog.js +2 -7
- package/dist/modules/sales/components/documents/ReturnEditDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnsSection.js +2 -7
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +3 -8
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/commands/users.ts +14 -1
- package/src/modules/auth/data/entities.ts +2 -2
- package/src/modules/auth/lib/backendChrome.tsx +46 -2
- package/src/modules/auth/lib/profile-sections.tsx +14 -3
- package/src/modules/auth/migrations/.snapshot-open-mercato.json +4 -2
- package/src/modules/auth/migrations/Migration20260907120000_auth.ts +21 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +2 -2
- package/src/modules/customer_accounts/AGENTS.md +3 -1
- package/src/modules/customer_accounts/api/admin/demo-accounts.ts +149 -0
- package/src/modules/customer_accounts/api/invitations/accept.ts +23 -6
- package/src/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.tsx +43 -41
- package/src/modules/customer_accounts/backend/customer_accounts/useDemoPortalAccounts.ts +42 -0
- package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +10 -3
- package/src/modules/customer_accounts/data/entities.ts +5 -1
- package/src/modules/customer_accounts/i18n/de.json +4 -2
- package/src/modules/customer_accounts/i18n/en.json +4 -2
- package/src/modules/customer_accounts/i18n/es.json +4 -2
- package/src/modules/customer_accounts/i18n/ko.json +4 -2
- package/src/modules/customer_accounts/i18n/pl.json +4 -2
- package/src/modules/customer_accounts/lib/exampleAccounts.ts +18 -0
- package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +5 -7
- package/src/modules/customer_accounts/migrations/Migration20260827160000_customer_accounts.ts +23 -0
- package/src/modules/customer_accounts/services/customerInvitationService.ts +89 -5
- package/src/modules/customer_accounts/setup.ts +2 -6
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +74 -41
- package/src/modules/customers/commands/people.ts +15 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +19 -44
- package/src/modules/data_sync/lib/sync-schedule-service.ts +32 -26
- package/src/modules/entities/api/records.ts +24 -15
- package/src/modules/entities/lib/scoped-field-defs.ts +76 -0
- package/src/modules/entities/lib/validation.ts +5 -45
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +33 -62
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +13 -37
- package/src/modules/notifications/backend/profile/notification-preferences/page.meta.ts +2 -2
- package/src/modules/progress/AGENTS.md +1 -1
- package/src/modules/progress/lib/progressServiceImpl.ts +26 -3
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +19 -25
- package/src/modules/sales/components/documents/LineItemDialog.tsx +53 -9
- package/src/modules/sales/components/documents/PaymentsSection.tsx +8 -8
- package/src/modules/sales/components/documents/ReturnEditDialog.tsx +2 -8
- package/src/modules/sales/components/documents/ReturnsSection.tsx +8 -7
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +9 -8
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/ko.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
extractCustomFieldValues,
|
|
57
57
|
} from "./customFieldHelpers";
|
|
58
58
|
import { canonicalizeUnitCode } from "@open-mercato/shared/lib/units/unitCodes";
|
|
59
|
+
import { parseLocaleNumber } from "@open-mercato/shared/lib/number";
|
|
59
60
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
60
61
|
|
|
61
62
|
const logger = createLogger('sales')
|
|
@@ -490,6 +491,30 @@ export function LineItemDialog({
|
|
|
490
491
|
}: SalesLineDialogProps) {
|
|
491
492
|
const t = useT();
|
|
492
493
|
const locale = useLocale();
|
|
494
|
+
// These fields are hand-rolled text inputs, so the raw string a user typed reaches the
|
|
495
|
+
// submit handler. It carries the separator the surrounding UI displays, which follows the
|
|
496
|
+
// application locale — `110,70` under Polish (issue #5552). A blank field keeps its old
|
|
497
|
+
// "must be greater than 0" message; only genuinely unparseable input returns null.
|
|
498
|
+
const parseUserNumber = React.useCallback(
|
|
499
|
+
(value: unknown): number | null => {
|
|
500
|
+
if (value == null) return 0;
|
|
501
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
502
|
+
if (typeof value !== "string") return null;
|
|
503
|
+
if (!value.trim()) return 0;
|
|
504
|
+
return parseLocaleNumber(value, locale);
|
|
505
|
+
},
|
|
506
|
+
[locale],
|
|
507
|
+
);
|
|
508
|
+
const numberExample = React.useMemo(() => {
|
|
509
|
+
try {
|
|
510
|
+
return new Intl.NumberFormat(locale, {
|
|
511
|
+
minimumFractionDigits: 2,
|
|
512
|
+
maximumFractionDigits: 2,
|
|
513
|
+
}).format(110.7);
|
|
514
|
+
} catch {
|
|
515
|
+
return "110.70";
|
|
516
|
+
}
|
|
517
|
+
}, [locale]);
|
|
493
518
|
const scope = useOrganizationScopeDetail();
|
|
494
519
|
const resolvedOrganizationId = organizationId ?? scope.organizationId ?? null;
|
|
495
520
|
const resolvedTenantId = tenantId ?? scope.tenantId ?? null;
|
|
@@ -952,7 +977,7 @@ export function LineItemDialog({
|
|
|
952
977
|
try {
|
|
953
978
|
const params = new URLSearchParams({ productId, pageSize: "20" });
|
|
954
979
|
if (variantId) params.set("variantId", variantId);
|
|
955
|
-
const quantityValue =
|
|
980
|
+
const quantityValue = parseUserNumber(quantity) ?? Number.NaN;
|
|
956
981
|
if (Number.isFinite(quantityValue) && quantityValue > 0) {
|
|
957
982
|
params.set("quantity", String(quantityValue));
|
|
958
983
|
}
|
|
@@ -1093,7 +1118,7 @@ export function LineItemDialog({
|
|
|
1093
1118
|
setPriceLoading(false);
|
|
1094
1119
|
}
|
|
1095
1120
|
},
|
|
1096
|
-
[t],
|
|
1121
|
+
[parseUserNumber, t],
|
|
1097
1122
|
);
|
|
1098
1123
|
|
|
1099
1124
|
const selectPriceAfterRefresh = React.useCallback(
|
|
@@ -1136,7 +1161,7 @@ export function LineItemDialog({
|
|
|
1136
1161
|
fromUnit: string | null | undefined,
|
|
1137
1162
|
toUnit: string | null | undefined,
|
|
1138
1163
|
): string | null => {
|
|
1139
|
-
const amount =
|
|
1164
|
+
const amount = parseUserNumber(rawUnitPrice) ?? Number.NaN;
|
|
1140
1165
|
if (!Number.isFinite(amount) || amount <= 0) return null;
|
|
1141
1166
|
const fromCode = normalizeUnitCode(fromUnit);
|
|
1142
1167
|
const toCode = normalizeUnitCode(toUnit);
|
|
@@ -1156,7 +1181,7 @@ export function LineItemDialog({
|
|
|
1156
1181
|
if (!Number.isFinite(convertedAmount) || convertedAmount <= 0) return null;
|
|
1157
1182
|
return normalizeUnitPriceInputValue(convertedAmount);
|
|
1158
1183
|
},
|
|
1159
|
-
[resolveUnitPriceFactor],
|
|
1184
|
+
[parseUserNumber, resolveUnitPriceFactor],
|
|
1160
1185
|
);
|
|
1161
1186
|
|
|
1162
1187
|
const applyPriceSelection = React.useCallback(
|
|
@@ -1342,8 +1367,16 @@ export function LineItemDialog({
|
|
|
1342
1367
|
);
|
|
1343
1368
|
}
|
|
1344
1369
|
|
|
1345
|
-
const qtyNumber =
|
|
1346
|
-
if (
|
|
1370
|
+
const qtyNumber = parseUserNumber(values.quantity);
|
|
1371
|
+
if (qtyNumber === null) {
|
|
1372
|
+
const message = t(
|
|
1373
|
+
"sales.documents.items.errorQuantityInvalid",
|
|
1374
|
+
"Enter the quantity as a number, for example {{example}}.",
|
|
1375
|
+
{ example: numberExample },
|
|
1376
|
+
);
|
|
1377
|
+
throw createCrudFormError(message, { quantity: message });
|
|
1378
|
+
}
|
|
1379
|
+
if (qtyNumber <= 0) {
|
|
1347
1380
|
throw createCrudFormError(
|
|
1348
1381
|
t(
|
|
1349
1382
|
"sales.documents.items.errorQuantity",
|
|
@@ -1380,8 +1413,16 @@ export function LineItemDialog({
|
|
|
1380
1413
|
);
|
|
1381
1414
|
})();
|
|
1382
1415
|
|
|
1383
|
-
const unitPriceNumber =
|
|
1384
|
-
if (
|
|
1416
|
+
const unitPriceNumber = parseUserNumber(values.unitPrice);
|
|
1417
|
+
if (unitPriceNumber === null) {
|
|
1418
|
+
const message = t(
|
|
1419
|
+
"sales.documents.items.errorUnitPriceInvalid",
|
|
1420
|
+
"Enter the unit price as a number, for example {{example}}.",
|
|
1421
|
+
{ example: numberExample },
|
|
1422
|
+
);
|
|
1423
|
+
throw createCrudFormError(message, { unitPrice: message });
|
|
1424
|
+
}
|
|
1425
|
+
if (unitPriceNumber <= 0) {
|
|
1385
1426
|
throw createCrudFormError(
|
|
1386
1427
|
t(
|
|
1387
1428
|
"sales.documents.items.errorUnitPrice",
|
|
@@ -1610,6 +1651,8 @@ export function LineItemDialog({
|
|
|
1610
1651
|
variantOption,
|
|
1611
1652
|
onSaved,
|
|
1612
1653
|
closeDialog,
|
|
1654
|
+
numberExample,
|
|
1655
|
+
parseUserNumber,
|
|
1613
1656
|
resolvedOrganizationId,
|
|
1614
1657
|
resolvedTenantId,
|
|
1615
1658
|
],
|
|
@@ -2541,7 +2584,7 @@ export function LineItemDialog({
|
|
|
2541
2584
|
layout: "full",
|
|
2542
2585
|
component: ({ values }: FieldRenderProps) => {
|
|
2543
2586
|
if (isCustomLine) return null;
|
|
2544
|
-
const quantity =
|
|
2587
|
+
const quantity = parseUserNumber(values?.quantity) ?? Number.NaN;
|
|
2545
2588
|
const enteredUnit = normalizeUnitCode(values?.quantityUnit);
|
|
2546
2589
|
if (!Number.isFinite(quantity) || quantity <= 0 || !enteredUnit) {
|
|
2547
2590
|
return (
|
|
@@ -2651,6 +2694,7 @@ export function LineItemDialog({
|
|
|
2651
2694
|
selectPriceAfterRefresh,
|
|
2652
2695
|
hasTaxMetadata,
|
|
2653
2696
|
isShippedOrderLine,
|
|
2697
|
+
parseUserNumber,
|
|
2654
2698
|
]);
|
|
2655
2699
|
|
|
2656
2700
|
const groups = React.useMemo<CrudFormGroup[]>(() => {
|
|
@@ -11,6 +11,7 @@ import { handleSectionMutationError, readRowUpdatedAt } from './optimisticLock'
|
|
|
11
11
|
import type { SectionAction } from '@open-mercato/ui/backend/detail'
|
|
12
12
|
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
13
13
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
14
|
+
import { formatDisplayDate, formatDisplayDateTime, toUtcDateInputValue } from '@open-mercato/ui/primitives/date-format'
|
|
14
15
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
15
16
|
import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
16
17
|
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
@@ -293,19 +294,18 @@ export function SalesDocumentPaymentsSection({
|
|
|
293
294
|
{
|
|
294
295
|
accessorKey: 'receivedAt',
|
|
295
296
|
header: t('sales.documents.payments.receivedAt', 'Received'),
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
297
|
+
// `receivedAt` is written from a date input (`PaymentDialog`), coerced with
|
|
298
|
+
// `z.coerce.date()` and stored as UTC midnight, so its day must be read in UTC.
|
|
299
|
+
// Reading it locally names the previous day west of UTC — and disagrees with the
|
|
300
|
+
// Edit dialog, which seeds from `receivedAt.slice(0, 10)`, i.e. the UTC day.
|
|
301
|
+
cell: ({ row }) => formatDisplayDate(toUtcDateInputValue(row.original.receivedAt), locale) ?? '—',
|
|
300
302
|
},
|
|
301
303
|
{
|
|
302
304
|
accessorKey: 'createdAt',
|
|
303
305
|
header: t('sales.documents.payments.createdAt', 'Created'),
|
|
304
|
-
cell: ({ row }) =>
|
|
305
|
-
row.original.createdAt ? new Date(row.original.createdAt).toLocaleString(locale) : '—',
|
|
306
|
+
cell: ({ row }) => formatDisplayDateTime(row.original.createdAt, locale) ?? '—',
|
|
306
307
|
meta: {
|
|
307
|
-
tooltipContent: (row: PaymentRow) =>
|
|
308
|
-
row.createdAt ? new Date(row.createdAt).toLocaleString(locale) : undefined,
|
|
308
|
+
tooltipContent: (row: PaymentRow) => formatDisplayDateTime(row.createdAt, locale) ?? undefined,
|
|
309
309
|
},
|
|
310
310
|
},
|
|
311
311
|
{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
+
import { toUtcDateInputValue } from '@open-mercato/ui/primitives/date-format'
|
|
4
5
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
|
|
5
6
|
import { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'
|
|
6
7
|
import { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'
|
|
@@ -36,13 +37,6 @@ type ReturnEditDialogProps = {
|
|
|
36
37
|
onSaved: () => Promise<void>
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
function toDateInputValue(value: string | null | undefined): string {
|
|
40
|
-
if (!value) return ''
|
|
41
|
-
const date = new Date(value)
|
|
42
|
-
if (Number.isNaN(date.getTime())) return ''
|
|
43
|
-
return date.toISOString().slice(0, 10)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
40
|
export function ReturnEditDialog({
|
|
47
41
|
open,
|
|
48
42
|
returnRecord,
|
|
@@ -62,7 +56,7 @@ export function ReturnEditDialog({
|
|
|
62
56
|
updatedAt: returnRecord?.updatedAt ?? null,
|
|
63
57
|
reason: returnRecord?.reason ?? '',
|
|
64
58
|
notes: returnRecord?.notes ?? '',
|
|
65
|
-
returnedAt:
|
|
59
|
+
returnedAt: toUtcDateInputValue(returnRecord?.returnedAt) ?? '',
|
|
66
60
|
}),
|
|
67
61
|
[returnRecord],
|
|
68
62
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
+
import { formatDisplayDate, toUtcDateInputValue } from '@open-mercato/ui/primitives/date-format'
|
|
4
5
|
import { Undo2, Plus } from 'lucide-react'
|
|
5
6
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
6
7
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
@@ -41,12 +42,8 @@ type SalesReturnsSectionProps = {
|
|
|
41
42
|
documentUpdatedAt?: string | null
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const date = new Date(value)
|
|
47
|
-
if (Number.isNaN(date.getTime())) return null
|
|
48
|
-
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date)
|
|
49
|
-
}
|
|
45
|
+
// One date formatter for this page rather than three with incompatible semantics.
|
|
46
|
+
export { formatDisplayDate }
|
|
50
47
|
|
|
51
48
|
export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }: SalesReturnsSectionProps) {
|
|
52
49
|
const t = useT()
|
|
@@ -329,7 +326,11 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
|
|
|
329
326
|
</div>
|
|
330
327
|
</div>
|
|
331
328
|
<div className="whitespace-nowrap text-right text-sm text-muted-foreground">
|
|
332
|
-
{
|
|
329
|
+
{/* `returnedAt` is submitted as a bare `yyyy-MM-dd` by `ReturnEditDialog`, coerced by
|
|
330
|
+
`z.coerce.date()` and returned via `.toISOString()`, and that dialog seeds itself
|
|
331
|
+
back from the UTC day — unconditionally, however the row was written. Matching the
|
|
332
|
+
dialog is the invariant, so the row cannot contradict itself west of UTC. */}
|
|
333
|
+
{formatDisplayDate(toUtcDateInputValue(ret.returnedAt), locale) ?? t('sales.returns.notSet', 'Not set')}
|
|
333
334
|
</div>
|
|
334
335
|
<div className="whitespace-nowrap text-right text-sm font-medium">
|
|
335
336
|
{formatMoney(ret.total, currencyCode ?? null, locale)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
+
import { formatDisplayDate, toUtcDateInputValue } from '@open-mercato/ui/primitives/date-format'
|
|
4
5
|
import { Pencil, Plus, Trash2, Truck } from 'lucide-react'
|
|
5
6
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
6
7
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
@@ -41,12 +42,8 @@ type SalesShipmentsSectionProps = {
|
|
|
41
42
|
onAddComment?: (body: string) => Promise<void>
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const date = new Date(value)
|
|
47
|
-
if (Number.isNaN(date.getTime())) return null
|
|
48
|
-
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date)
|
|
49
|
-
}
|
|
45
|
+
// One date formatter for this page rather than three with incompatible semantics.
|
|
46
|
+
export { formatDisplayDate }
|
|
50
47
|
|
|
51
48
|
const formatShipmentAddress = (metadata?: Record<string, unknown> | null): string | null => {
|
|
52
49
|
if (!metadata || typeof metadata !== 'object') return null
|
|
@@ -487,8 +484,12 @@ export function SalesShipmentsSection({
|
|
|
487
484
|
) : (
|
|
488
485
|
<div className="grid gap-3 md:grid-cols-2">
|
|
489
486
|
{shipments.map((shipment) => {
|
|
490
|
-
|
|
491
|
-
|
|
487
|
+
// `shippedAt` / `deliveredAt` are written from date inputs (`ShipmentDialog`), coerced
|
|
488
|
+
// by `z.coerce.date()` and stored as UTC midnight, and the dialog seeds itself back from
|
|
489
|
+
// `.slice(0, 10)` — the UTC day. Reading them locally names the previous day west of UTC
|
|
490
|
+
// and disagrees with that dialog on the same row.
|
|
491
|
+
const shippedAt = formatDisplayDate(toUtcDateInputValue(shipment.shippedAt), locale)
|
|
492
|
+
const deliveredAt = formatDisplayDate(toUtcDateInputValue(shipment.deliveredAt), locale)
|
|
492
493
|
const addressSummary = formatShipmentAddress(shipment.metadata)
|
|
493
494
|
const statusLabel =
|
|
494
495
|
shipment.statusLabel ??
|
|
@@ -1016,10 +1016,12 @@
|
|
|
1016
1016
|
"sales.documents.items.errorProductRequired": "Wähle ein Produkt aus, um fortzufahren.",
|
|
1017
1017
|
"sales.documents.items.errorQuantity": "Die Menge muss größer als 0 sein.",
|
|
1018
1018
|
"sales.documents.items.errorQuantityBelowShipped": "Die Menge kann nicht unter die bereits versandten {{shipped}} gesenkt werden.",
|
|
1019
|
+
"sales.documents.items.errorQuantityInvalid": "Geben Sie die Menge als Zahl ein, zum Beispiel {{example}}.",
|
|
1019
1020
|
"sales.documents.items.errorQuantityShipmentsUnknown": "Die Menge kann erst gesenkt werden, wenn die Sendungen dieses Auftrags geladen wurden. Öffnen Sie den Auftrag erneut, um es noch einmal zu versuchen.",
|
|
1020
1021
|
"sales.documents.items.errorSave": "Position konnte nicht gespeichert werden.",
|
|
1021
1022
|
"sales.documents.items.errorScope": "Organisation und Mandant sind erforderlich.",
|
|
1022
1023
|
"sales.documents.items.errorUnitPrice": "Der Stückpreis muss größer als 0 sein.",
|
|
1024
|
+
"sales.documents.items.errorUnitPriceInvalid": "Geben Sie den Stückpreis als Zahl ein, zum Beispiel {{example}}.",
|
|
1023
1025
|
"sales.documents.items.errorVariantRequired": "Wähle eine Variante aus, um fortzufahren.",
|
|
1024
1026
|
"sales.documents.items.errors.uom.conversionNotFound": "Keine Einheitenumrechnung für dieses Produkt gefunden.",
|
|
1025
1027
|
"sales.documents.items.errors.uom.defaultUnitMissing": "Dem Produkt fehlt eine Standard-Basiseinheit.",
|
|
@@ -1016,10 +1016,12 @@
|
|
|
1016
1016
|
"sales.documents.items.errorProductRequired": "Select a product to continue.",
|
|
1017
1017
|
"sales.documents.items.errorQuantity": "Quantity must be greater than 0.",
|
|
1018
1018
|
"sales.documents.items.errorQuantityBelowShipped": "You cannot lower the quantity below the {{shipped}} already shipped.",
|
|
1019
|
+
"sales.documents.items.errorQuantityInvalid": "Enter the quantity as a number, for example {{example}}.",
|
|
1019
1020
|
"sales.documents.items.errorQuantityShipmentsUnknown": "The quantity cannot be lowered until this order's shipments have been read. Reopen the order to try again.",
|
|
1020
1021
|
"sales.documents.items.errorSave": "Failed to save line.",
|
|
1021
1022
|
"sales.documents.items.errorScope": "Organization and tenant are required.",
|
|
1022
1023
|
"sales.documents.items.errorUnitPrice": "Unit price must be greater than 0.",
|
|
1024
|
+
"sales.documents.items.errorUnitPriceInvalid": "Enter the unit price as a number, for example {{example}}.",
|
|
1023
1025
|
"sales.documents.items.errorVariantRequired": "Select a variant to continue.",
|
|
1024
1026
|
"sales.documents.items.errors.uom.conversionNotFound": "No unit conversion found for this product.",
|
|
1025
1027
|
"sales.documents.items.errors.uom.defaultUnitMissing": "Product is missing a default base unit.",
|
|
@@ -1016,10 +1016,12 @@
|
|
|
1016
1016
|
"sales.documents.items.errorProductRequired": "Selecciona un producto para continuar.",
|
|
1017
1017
|
"sales.documents.items.errorQuantity": "La cantidad debe ser mayor que 0.",
|
|
1018
1018
|
"sales.documents.items.errorQuantityBelowShipped": "No puedes reducir la cantidad por debajo de las {{shipped}} ya enviadas.",
|
|
1019
|
+
"sales.documents.items.errorQuantityInvalid": "Introduce la cantidad como un número, por ejemplo {{example}}.",
|
|
1019
1020
|
"sales.documents.items.errorQuantityShipmentsUnknown": "La cantidad no se puede reducir hasta que se carguen los envíos de este pedido. Vuelve a abrir el pedido para intentarlo de nuevo.",
|
|
1020
1021
|
"sales.documents.items.errorSave": "No se pudo guardar la línea.",
|
|
1021
1022
|
"sales.documents.items.errorScope": "Se requieren organización e inquilino.",
|
|
1022
1023
|
"sales.documents.items.errorUnitPrice": "El precio unitario debe ser mayor que 0.",
|
|
1024
|
+
"sales.documents.items.errorUnitPriceInvalid": "Introduce el precio unitario como un número, por ejemplo {{example}}.",
|
|
1023
1025
|
"sales.documents.items.errorVariantRequired": "Selecciona una variante para continuar.",
|
|
1024
1026
|
"sales.documents.items.errors.uom.conversionNotFound": "No se encontró conversión de unidad para este producto.",
|
|
1025
1027
|
"sales.documents.items.errors.uom.defaultUnitMissing": "El producto no tiene una unidad base predeterminada.",
|
|
@@ -1016,10 +1016,12 @@
|
|
|
1016
1016
|
"sales.documents.items.errorProductRequired": "계속하려면 상품을 선택하세요.",
|
|
1017
1017
|
"sales.documents.items.errorQuantity": "수량은 0보다 커야 합니다.",
|
|
1018
1018
|
"sales.documents.items.errorQuantityBelowShipped": "이미 배송된 {{shipped}} 이하로 수량을 줄일 수 없습니다.",
|
|
1019
|
+
"sales.documents.items.errorQuantityInvalid": "수량을 숫자로 입력하세요. 예: {{example}}.",
|
|
1019
1020
|
"sales.documents.items.errorQuantityShipmentsUnknown": "이 주문의 배송 정보를 불러올 때까지 수량을 줄일 수 없습니다. 주문을 다시 열어 재시도하세요.",
|
|
1020
1021
|
"sales.documents.items.errorSave": "품목을 저장하지 못했습니다.",
|
|
1021
1022
|
"sales.documents.items.errorScope": "조직 및 테넌트 정보가 필요합니다.",
|
|
1022
1023
|
"sales.documents.items.errorUnitPrice": "단가는 0보다 커야 합니다.",
|
|
1024
|
+
"sales.documents.items.errorUnitPriceInvalid": "단가를 숫자로 입력하세요. 예: {{example}}.",
|
|
1023
1025
|
"sales.documents.items.errorVariantRequired": "계속하려면 옵션(변형)을 선택하세요.",
|
|
1024
1026
|
"sales.documents.items.errors.uom.conversionNotFound": "이 상품에 대한 단위 변환 정보를 찾을 수 없습니다.",
|
|
1025
1027
|
"sales.documents.items.errors.uom.defaultUnitMissing": "상품에 기본 단위가 설정되어 있지 않습니다.",
|
|
@@ -1016,10 +1016,12 @@
|
|
|
1016
1016
|
"sales.documents.items.errorProductRequired": "Wybierz produkt, aby kontynuować.",
|
|
1017
1017
|
"sales.documents.items.errorQuantity": "Ilość musi być większa niż 0.",
|
|
1018
1018
|
"sales.documents.items.errorQuantityBelowShipped": "Nie można obniżyć ilości poniżej {{shipped}} już wysłanych.",
|
|
1019
|
+
"sales.documents.items.errorQuantityInvalid": "Podaj ilość jako liczbę, na przykład {{example}}.",
|
|
1019
1020
|
"sales.documents.items.errorQuantityShipmentsUnknown": "Nie można obniżyć ilości, dopóki wysyłki tego zamówienia nie zostaną wczytane. Otwórz zamówienie ponownie, aby spróbować jeszcze raz.",
|
|
1020
1021
|
"sales.documents.items.errorSave": "Nie udało się zapisać pozycji.",
|
|
1021
1022
|
"sales.documents.items.errorScope": "Wymagane są organizacja i najemca.",
|
|
1022
1023
|
"sales.documents.items.errorUnitPrice": "Cena jednostkowa musi być większa niż 0.",
|
|
1024
|
+
"sales.documents.items.errorUnitPriceInvalid": "Podaj cenę jednostkową jako liczbę, na przykład {{example}}.",
|
|
1023
1025
|
"sales.documents.items.errorVariantRequired": "Wybierz wariant, aby kontynuować.",
|
|
1024
1026
|
"sales.documents.items.errors.uom.conversionNotFound": "Nie znaleziono przelicznika jednostek dla tego produktu.",
|
|
1025
1027
|
"sales.documents.items.errors.uom.defaultUnitMissing": "Produkt nie ma zdefiniowanej domyślnej jednostki bazowej.",
|