@open-mercato/core 0.7.1-develop.7195.1.b5efd1d51a → 0.7.1-develop.7201.1.9bd6c88e3d
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/auth/lib/setup-app.js +1 -0
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductUomSection.js +27 -22
- package/dist/modules/catalog/components/products/ProductUomSection.js.map +2 -2
- package/dist/modules/entities/lib/field-definitions.js +9 -0
- package/dist/modules/entities/lib/field-definitions.js.map +2 -2
- package/dist/modules/entities/lib/install-from-ce.js +1 -0
- package/dist/modules/entities/lib/install-from-ce.js.map +2 -2
- package/dist/modules/eudr/search.js +3 -5
- package/dist/modules/eudr/search.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentDialog.js +14 -11
- package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
- package/dist/modules/sales/lib/lineSnapshots.js +3 -0
- package/dist/modules/sales/lib/lineSnapshots.js.map +2 -2
- package/dist/modules/wms/components/backend/AdjustInventoryDialog.js +8 -6
- package/dist/modules/wms/components/backend/AdjustInventoryDialog.js.map +2 -2
- package/dist/modules/wms/search.js +4 -4
- package/dist/modules/wms/search.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/lib/setup-app.ts +6 -0
- package/src/modules/catalog/components/products/ProductUomSection.tsx +31 -21
- package/src/modules/entities/lib/field-definitions.ts +15 -0
- package/src/modules/entities/lib/install-from-ce.ts +1 -0
- package/src/modules/eudr/i18n/de.json +4 -1
- package/src/modules/eudr/i18n/en.json +4 -1
- package/src/modules/eudr/i18n/es.json +4 -1
- package/src/modules/eudr/i18n/ko.json +4 -1
- package/src/modules/eudr/i18n/pl.json +4 -1
- package/src/modules/eudr/search.ts +3 -5
- package/src/modules/sales/components/documents/PaymentDialog.tsx +18 -11
- 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
- package/src/modules/sales/lib/lineSnapshots.ts +22 -3
- package/src/modules/wms/components/backend/AdjustInventoryDialog.tsx +6 -5
- package/src/modules/wms/i18n/de.json +4 -0
- package/src/modules/wms/i18n/en.json +4 -0
- package/src/modules/wms/i18n/es.json +4 -0
- package/src/modules/wms/i18n/ko.json +4 -0
- package/src/modules/wms/i18n/pl.json +4 -0
- package/src/modules/wms/search.ts +4 -4
|
@@ -36,6 +36,7 @@ const CONFIG_PASSTHROUGH_KEYS: Array<keyof CustomFieldDefinition> = [
|
|
|
36
36
|
'formEditable',
|
|
37
37
|
'listVisible',
|
|
38
38
|
'indexed',
|
|
39
|
+
'encrypted',
|
|
39
40
|
'priority',
|
|
40
41
|
'editor',
|
|
41
42
|
'input',
|
|
@@ -65,6 +66,12 @@ function configEquals(a: unknown, b: unknown): boolean {
|
|
|
65
66
|
return JSON.stringify(normalizeValue(a ?? null)) === JSON.stringify(normalizeValue(b ?? null))
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
function hasEncryptionEnabled(definition: CustomFieldDef | null): boolean {
|
|
70
|
+
const config = definition?.configJson
|
|
71
|
+
if (!config || typeof config !== 'object') return false
|
|
72
|
+
return (config as Record<string, unknown>).encrypted === true
|
|
73
|
+
}
|
|
74
|
+
|
|
68
75
|
export async function ensureCustomFieldDefinitions(
|
|
69
76
|
em: EntityManager,
|
|
70
77
|
sets: FieldSetInput[],
|
|
@@ -103,6 +110,14 @@ export async function ensureCustomFieldDefinitions(
|
|
|
103
110
|
if (value !== undefined) configJson[key] = value as unknown
|
|
104
111
|
}
|
|
105
112
|
|
|
113
|
+
// configJson is rebuilt from the declaration, so a field encrypted through the
|
|
114
|
+
// definitions API would lose the flag on the next install while its stored
|
|
115
|
+
// values stay ciphertext the read path no longer decrypts. Keep an enabled flag
|
|
116
|
+
// unless the declaration turns it off with an explicit `encrypted: false`.
|
|
117
|
+
if (configJson.encrypted === undefined && hasEncryptionEnabled(existing)) {
|
|
118
|
+
configJson.encrypted = true
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
if (!existing) {
|
|
107
122
|
if (!scope.dryRun) {
|
|
108
123
|
const createdDef = em.create(CustomFieldDef, {
|
|
@@ -744,5 +744,8 @@
|
|
|
744
744
|
"eudr.supplierPanel.viewSubmissions": "Einreichungen anzeigen",
|
|
745
745
|
"eudr.supplierPanel.warnings": "Mit Warnungen",
|
|
746
746
|
"eudr.warnings.harvestBeforeCutoff": "Ernte vor dem EUDR-Stichtag",
|
|
747
|
-
"eudr.warnings.speciesMissing": "Bei einer verknüpften Holz-Zuordnung fehlt der wissenschaftliche oder gebräuchliche Artname"
|
|
747
|
+
"eudr.warnings.speciesMissing": "Bei einer verknüpften Holz-Zuordnung fehlt der wissenschaftliche oder gebräuchliche Artname",
|
|
748
|
+
"search.entityType.eudr.eudr_due_diligence_statement": "DDS-Erklärung",
|
|
749
|
+
"search.entityType.eudr.eudr_evidence_submission": "Nachweiseinreichung",
|
|
750
|
+
"search.entityType.eudr.eudr_plot": "Parzelle"
|
|
748
751
|
}
|
|
@@ -744,5 +744,8 @@
|
|
|
744
744
|
"eudr.supplierPanel.viewSubmissions": "View submissions",
|
|
745
745
|
"eudr.supplierPanel.warnings": "With warnings",
|
|
746
746
|
"eudr.warnings.harvestBeforeCutoff": "Harvest before EUDR cutoff",
|
|
747
|
-
"eudr.warnings.speciesMissing": "A linked wood mapping is missing a scientific or common species name"
|
|
747
|
+
"eudr.warnings.speciesMissing": "A linked wood mapping is missing a scientific or common species name",
|
|
748
|
+
"search.entityType.eudr.eudr_due_diligence_statement": "DDS statement",
|
|
749
|
+
"search.entityType.eudr.eudr_evidence_submission": "Evidence submission",
|
|
750
|
+
"search.entityType.eudr.eudr_plot": "Plot"
|
|
748
751
|
}
|
|
@@ -744,5 +744,8 @@
|
|
|
744
744
|
"eudr.supplierPanel.viewSubmissions": "Ver presentaciones",
|
|
745
745
|
"eudr.supplierPanel.warnings": "Con advertencias",
|
|
746
746
|
"eudr.warnings.harvestBeforeCutoff": "Cosecha anterior a la fecha de corte EUDR",
|
|
747
|
-
"eudr.warnings.speciesMissing": "A una asignación de madera vinculada le falta el nombre científico o común de la especie"
|
|
747
|
+
"eudr.warnings.speciesMissing": "A una asignación de madera vinculada le falta el nombre científico o común de la especie",
|
|
748
|
+
"search.entityType.eudr.eudr_due_diligence_statement": "Declaración DDS",
|
|
749
|
+
"search.entityType.eudr.eudr_evidence_submission": "Envío de evidencia",
|
|
750
|
+
"search.entityType.eudr.eudr_plot": "Parcela"
|
|
748
751
|
}
|
|
@@ -744,5 +744,8 @@
|
|
|
744
744
|
"eudr.supplierPanel.viewSubmissions": "제출물 보기",
|
|
745
745
|
"eudr.supplierPanel.warnings": "경고 있음",
|
|
746
746
|
"eudr.warnings.harvestBeforeCutoff": "EUDR 기준일 이전 수확",
|
|
747
|
-
"eudr.warnings.speciesMissing": "연결된 목재 매핑에 학명 또는 일반명이 누락되었습니다"
|
|
747
|
+
"eudr.warnings.speciesMissing": "연결된 목재 매핑에 학명 또는 일반명이 누락되었습니다",
|
|
748
|
+
"search.entityType.eudr.eudr_due_diligence_statement": "DDS 진술서",
|
|
749
|
+
"search.entityType.eudr.eudr_evidence_submission": "증거 제출물",
|
|
750
|
+
"search.entityType.eudr.eudr_plot": "구획"
|
|
748
751
|
}
|
|
@@ -744,5 +744,8 @@
|
|
|
744
744
|
"eudr.supplierPanel.viewSubmissions": "Zobacz zgłoszenia",
|
|
745
745
|
"eudr.supplierPanel.warnings": "Z ostrzeżeniami",
|
|
746
746
|
"eudr.warnings.harvestBeforeCutoff": "Zbiory przed datą graniczną EUDR",
|
|
747
|
-
"eudr.warnings.speciesMissing": "W powiązanym mapowaniu drewna brakuje naukowej lub zwyczajowej nazwy gatunku"
|
|
747
|
+
"eudr.warnings.speciesMissing": "W powiązanym mapowaniu drewna brakuje naukowej lub zwyczajowej nazwy gatunku",
|
|
748
|
+
"search.entityType.eudr.eudr_due_diligence_statement": "Oświadczenie DDS",
|
|
749
|
+
"search.entityType.eudr.eudr_evidence_submission": "Zgłoszenie dowodów",
|
|
750
|
+
"search.entityType.eudr.eudr_plot": "Działka"
|
|
748
751
|
}
|
|
@@ -95,23 +95,21 @@ function resolveSupplierName(record: Record<string, unknown>): string | null {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function buildStatementPresenter(translate: TranslateFn, record: Record<string, unknown>): SearchResultPresenter {
|
|
98
|
-
const label = translate('eudr.search.badge.statement', 'DDS statement')
|
|
99
98
|
const title = readRecordText(record, 'title') ?? translate('eudr.common.recordUnavailable')
|
|
100
99
|
const subtitle = formatSubtitle(
|
|
101
100
|
readRecordText(record, 'reference_number', 'referenceNumber'),
|
|
102
101
|
translateToken(translate, 'eudr.commodity', readRecordText(record, 'commodity')),
|
|
103
102
|
)
|
|
104
|
-
return { title, subtitle, icon: 'file-check'
|
|
103
|
+
return { title, subtitle, icon: 'file-check' }
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
function buildPlotPresenter(translate: TranslateFn, record: Record<string, unknown>): SearchResultPresenter {
|
|
108
|
-
const label = translate('eudr.search.badge.plot', 'Plot')
|
|
109
107
|
const title = readRecordText(record, 'name') ?? translate('eudr.common.recordUnavailable')
|
|
110
108
|
const subtitle = formatSubtitle(
|
|
111
109
|
readRecordText(record, 'origin_country', 'originCountry'),
|
|
112
110
|
translateToken(translate, 'eudr.plotType', readRecordText(record, 'plot_type', 'plotType')),
|
|
113
111
|
)
|
|
114
|
-
return { title, subtitle, icon: 'map-pin'
|
|
112
|
+
return { title, subtitle, icon: 'map-pin' }
|
|
115
113
|
}
|
|
116
114
|
|
|
117
115
|
function buildSubmissionPresenter(translate: TranslateFn, record: Record<string, unknown>): SearchResultPresenter {
|
|
@@ -125,7 +123,7 @@ function buildSubmissionPresenter(translate: TranslateFn, record: Record<string,
|
|
|
125
123
|
supplierName,
|
|
126
124
|
translateToken(translate, 'eudr.submissionStatus', readRecordText(record, 'status')),
|
|
127
125
|
)
|
|
128
|
-
return { title, subtitle, icon: 'package'
|
|
126
|
+
return { title, subtitle, icon: 'package' }
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
export const searchConfig: SearchModuleConfig = {
|
|
@@ -15,7 +15,8 @@ import { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'
|
|
|
15
15
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
16
16
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
17
17
|
import { E } from '#generated/entities.ids.generated'
|
|
18
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
18
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
19
|
+
import { parseLocaleNumber } from '@open-mercato/shared/lib/number'
|
|
19
20
|
import { normalizeCustomFieldSubmitValue, extractCustomFieldValues } from './customFieldHelpers'
|
|
20
21
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
21
22
|
|
|
@@ -66,13 +67,15 @@ type PaymentDialogProps = {
|
|
|
66
67
|
onSaved?: (totals?: PaymentTotals | null) => void | Promise<void>
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return 0
|
|
70
|
+
// The amount field is a hand-rolled text input, so the raw string a user typed reaches
|
|
71
|
+
// the submit handler carrying the separator the surrounding UI displays — `110,70` under
|
|
72
|
+
// Polish (issue #5828, same defect class as #5552). A blank/absent value keeps the old
|
|
73
|
+
// "must be a positive amount" message; only genuinely unparseable input returns null.
|
|
74
|
+
const parseAmountInput = (value: unknown, locale?: string): number | null => {
|
|
75
|
+
if (typeof value === 'number') return Number.isFinite(value) ? value : null
|
|
76
|
+
if (typeof value !== 'string') return 0
|
|
77
|
+
if (!value.trim()) return 0
|
|
78
|
+
return parseLocaleNumber(value, locale)
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
export function PaymentDialog({
|
|
@@ -88,6 +91,7 @@ export function PaymentDialog({
|
|
|
88
91
|
onSaved,
|
|
89
92
|
}: PaymentDialogProps) {
|
|
90
93
|
const t = useT()
|
|
94
|
+
const locale = useLocale()
|
|
91
95
|
const dialogContentRef = React.useRef<HTMLDivElement | null>(null)
|
|
92
96
|
const [formResetKey, setFormResetKey] = React.useState(0)
|
|
93
97
|
const [paymentMethods, setPaymentMethods] = React.useState<PaymentMethodOption[]>([])
|
|
@@ -352,9 +356,8 @@ export function PaymentDialog({
|
|
|
352
356
|
return (
|
|
353
357
|
<div className="flex items-center gap-2">
|
|
354
358
|
<Input
|
|
355
|
-
type="
|
|
359
|
+
type="text"
|
|
356
360
|
inputMode="decimal"
|
|
357
|
-
step="0.01"
|
|
358
361
|
value={normalized as string | number}
|
|
359
362
|
onChange={(event) => setValue(event.target.value)}
|
|
360
363
|
placeholder="0.00"
|
|
@@ -493,10 +496,14 @@ export function PaymentDialog({
|
|
|
493
496
|
const handleSubmit = React.useCallback(
|
|
494
497
|
async (values: Record<string, unknown>) => {
|
|
495
498
|
const resolvedCurrency = currencyCode ? currencyCode.toUpperCase() : ''
|
|
496
|
-
const amountValue =
|
|
499
|
+
const amountValue = parseAmountInput(values.amount, locale)
|
|
497
500
|
if (!resolvedCurrency.trim()) {
|
|
498
501
|
throw createCrudFormError(t('sales.documents.payments.currencyRequired', 'Currency is required.'))
|
|
499
502
|
}
|
|
503
|
+
if (amountValue === null) {
|
|
504
|
+
const message = t('sales.documents.payments.amountInvalid', 'Enter the amount as a number.')
|
|
505
|
+
throw createCrudFormError(message, { amount: message })
|
|
506
|
+
}
|
|
500
507
|
if (amountValue <= 0) {
|
|
501
508
|
throw createCrudFormError(t('sales.documents.payments.amountRequired', 'Enter a positive amount.'), {
|
|
502
509
|
amount: t('sales.documents.payments.amountRequired', 'Enter a positive amount.'),
|
|
@@ -1139,6 +1139,7 @@
|
|
|
1139
1139
|
"sales.documents.list.table.unassigned": "Nicht zugewiesen",
|
|
1140
1140
|
"sales.documents.payments.add": "Zahlung hinzufügen",
|
|
1141
1141
|
"sales.documents.payments.amount": "Betrag",
|
|
1142
|
+
"sales.documents.payments.amountInvalid": "Geben Sie den Betrag als Zahl ein.",
|
|
1142
1143
|
"sales.documents.payments.amountRequired": "Geben Sie einen positiven Betrag ein.",
|
|
1143
1144
|
"sales.documents.payments.created": "Zahlung erfasst.",
|
|
1144
1145
|
"sales.documents.payments.createdAt": "Erstellt",
|
|
@@ -1139,6 +1139,7 @@
|
|
|
1139
1139
|
"sales.documents.list.table.unassigned": "Unassigned",
|
|
1140
1140
|
"sales.documents.payments.add": "Add payment",
|
|
1141
1141
|
"sales.documents.payments.amount": "Amount",
|
|
1142
|
+
"sales.documents.payments.amountInvalid": "Enter the amount as a number.",
|
|
1142
1143
|
"sales.documents.payments.amountRequired": "Enter a positive amount.",
|
|
1143
1144
|
"sales.documents.payments.created": "Payment recorded.",
|
|
1144
1145
|
"sales.documents.payments.createdAt": "Created",
|
|
@@ -1139,6 +1139,7 @@
|
|
|
1139
1139
|
"sales.documents.list.table.unassigned": "Sin asignar",
|
|
1140
1140
|
"sales.documents.payments.add": "Agregar pago",
|
|
1141
1141
|
"sales.documents.payments.amount": "Importe",
|
|
1142
|
+
"sales.documents.payments.amountInvalid": "Introduce el importe como un número.",
|
|
1142
1143
|
"sales.documents.payments.amountRequired": "Introduce un importe positivo.",
|
|
1143
1144
|
"sales.documents.payments.created": "Pago registrado.",
|
|
1144
1145
|
"sales.documents.payments.createdAt": "Creado",
|
|
@@ -1139,6 +1139,7 @@
|
|
|
1139
1139
|
"sales.documents.list.table.unassigned": "미지정",
|
|
1140
1140
|
"sales.documents.payments.add": "결제 추가",
|
|
1141
1141
|
"sales.documents.payments.amount": "금액",
|
|
1142
|
+
"sales.documents.payments.amountInvalid": "금액을 숫자로 입력하세요.",
|
|
1142
1143
|
"sales.documents.payments.amountRequired": "0보다 큰 금액을 입력하세요.",
|
|
1143
1144
|
"sales.documents.payments.created": "결제가 등록되었습니다.",
|
|
1144
1145
|
"sales.documents.payments.createdAt": "등록일",
|
|
@@ -1139,6 +1139,7 @@
|
|
|
1139
1139
|
"sales.documents.list.table.unassigned": "Nieprzypisany",
|
|
1140
1140
|
"sales.documents.payments.add": "Dodaj płatność",
|
|
1141
1141
|
"sales.documents.payments.amount": "Kwota",
|
|
1142
|
+
"sales.documents.payments.amountInvalid": "Podaj kwotę jako liczbę.",
|
|
1142
1143
|
"sales.documents.payments.amountRequired": "Wprowadź dodatnią kwotę.",
|
|
1143
1144
|
"sales.documents.payments.created": "Płatność zarejestrowana.",
|
|
1144
1145
|
"sales.documents.payments.createdAt": "Utworzono",
|
|
@@ -11,9 +11,28 @@ function toNumeric(value: unknown): number {
|
|
|
11
11
|
return 0
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* A snapshot rebuilt from a persisted row, plus the columns the calculation
|
|
16
|
+
* engine never reads but the write paths must carry back to the row.
|
|
17
|
+
*
|
|
18
|
+
* The line upsert, line delete and adjustment write paths all rebuild *every*
|
|
19
|
+
* line of the document from these snapshots and then `Object.assign` the result
|
|
20
|
+
* onto the existing entities. Anything the mapper drops is therefore not merely
|
|
21
|
+
* absent from the calculation — it is written back as `null` over the stored
|
|
22
|
+
* value of lines the caller never touched (#5911).
|
|
23
|
+
*/
|
|
24
|
+
export type SalesPersistedLineSnapshot = SalesLineSnapshot & {
|
|
25
|
+
statusEntryId: string | null
|
|
26
|
+
catalogSnapshot: Record<string, unknown> | null
|
|
27
|
+
promotionSnapshot: Record<string, unknown> | null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function mapPersistedLine(line: SalesOrderLine | SalesQuoteLine): SalesPersistedLineSnapshot {
|
|
15
31
|
return {
|
|
16
32
|
id: line.id,
|
|
33
|
+
statusEntryId: line.statusEntryId ?? null,
|
|
34
|
+
catalogSnapshot: line.catalogSnapshot ? cloneJson(line.catalogSnapshot) : null,
|
|
35
|
+
promotionSnapshot: line.promotionSnapshot ? cloneJson(line.promotionSnapshot) : null,
|
|
17
36
|
lineNumber: line.lineNumber,
|
|
18
37
|
kind: line.kind,
|
|
19
38
|
productId: line.productId ?? null,
|
|
@@ -60,11 +79,11 @@ function mapPersistedLine(line: SalesOrderLine | SalesQuoteLine): SalesLineSnaps
|
|
|
60
79
|
* two files used to carry byte-identical copies, and that duplication is why
|
|
61
80
|
* the return flows kept the discount defect after the order flows were fixed.
|
|
62
81
|
*/
|
|
63
|
-
export function mapOrderLineEntityToSnapshot(line: SalesOrderLine):
|
|
82
|
+
export function mapOrderLineEntityToSnapshot(line: SalesOrderLine): SalesPersistedLineSnapshot {
|
|
64
83
|
return mapPersistedLine(line)
|
|
65
84
|
}
|
|
66
85
|
|
|
67
|
-
export function mapQuoteLineEntityToSnapshot(line: SalesQuoteLine):
|
|
86
|
+
export function mapQuoteLineEntityToSnapshot(line: SalesQuoteLine): SalesPersistedLineSnapshot {
|
|
68
87
|
return mapPersistedLine(line)
|
|
69
88
|
}
|
|
70
89
|
|
|
@@ -32,7 +32,8 @@ import {
|
|
|
32
32
|
} from '@open-mercato/ui/primitives/select'
|
|
33
33
|
import { StatusBadge } from '@open-mercato/ui/primitives/status-badge'
|
|
34
34
|
import { Textarea } from '@open-mercato/ui/primitives/textarea'
|
|
35
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
35
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
36
|
+
import { parseLocaleNumber } from '@open-mercato/shared/lib/number'
|
|
36
37
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
37
38
|
import { buildInventoryMutationReferenceId } from '../../lib/inventoryMutationUi'
|
|
38
39
|
import {
|
|
@@ -91,11 +92,10 @@ const EMPTY_FORM: AdjustFormValues = {
|
|
|
91
92
|
serialNumber: '',
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
function parseDeltaInput(value: string): number | null {
|
|
95
|
+
export function parseDeltaInput(value: string, locale?: string): number | null {
|
|
95
96
|
const trimmed = value.trim()
|
|
96
97
|
if (!trimmed || trimmed === '-' || trimmed === '+') return null
|
|
97
|
-
|
|
98
|
-
return Number.isFinite(parsed) ? parsed : null
|
|
98
|
+
return parseLocaleNumber(trimmed, locale)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
export function AdjustInventoryDialog({
|
|
@@ -108,6 +108,7 @@ export function AdjustInventoryDialog({
|
|
|
108
108
|
initialLotId,
|
|
109
109
|
}: AdjustInventoryDialogProps) {
|
|
110
110
|
const t = useT()
|
|
111
|
+
const locale = useLocale()
|
|
111
112
|
const queryClient = useQueryClient()
|
|
112
113
|
const formRef = React.useRef<HTMLFormElement>(null)
|
|
113
114
|
const { runMutation, retryLastMutation } = useGuardedMutation<{
|
|
@@ -690,7 +691,7 @@ export function AdjustInventoryDialog({
|
|
|
690
691
|
inputMode="decimal"
|
|
691
692
|
value={String(form.delta)}
|
|
692
693
|
onChange={(event) => {
|
|
693
|
-
const parsed = parseDeltaInput(event.target.value)
|
|
694
|
+
const parsed = parseDeltaInput(event.target.value, locale)
|
|
694
695
|
if (parsed == null) return
|
|
695
696
|
patchForm({ delta: parsed })
|
|
696
697
|
}}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
+
"search.entityType.wms.inventory_lot": "Lagerbestand",
|
|
3
|
+
"search.entityType.wms.product_inventory_profile": "Bestandsprofil",
|
|
4
|
+
"search.entityType.wms.warehouse": "Lager",
|
|
5
|
+
"search.entityType.wms.warehouse_location": "Lagerort",
|
|
2
6
|
"wms.audit.inventory.receive": "Wareneingang buchen",
|
|
3
7
|
"wms.audit.salesOrder.assignWarehouse": "Lager dem Auftrag zuweisen",
|
|
4
8
|
"wms.audit.salesOrder.reRunReservation": "Reservierung für Auftrag erneut ausführen",
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
+
"search.entityType.wms.inventory_lot": "Inventory lot",
|
|
3
|
+
"search.entityType.wms.product_inventory_profile": "Inventory profile",
|
|
4
|
+
"search.entityType.wms.warehouse": "Warehouse",
|
|
5
|
+
"search.entityType.wms.warehouse_location": "Location",
|
|
2
6
|
"wms.audit.inventory.receive": "Receive inventory",
|
|
3
7
|
"wms.audit.salesOrder.assignWarehouse": "Assign warehouse to order",
|
|
4
8
|
"wms.audit.salesOrder.reRunReservation": "Re-run reservation for order",
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
+
"search.entityType.wms.inventory_lot": "Lote de inventario",
|
|
3
|
+
"search.entityType.wms.product_inventory_profile": "Perfil de inventario",
|
|
4
|
+
"search.entityType.wms.warehouse": "Almacén",
|
|
5
|
+
"search.entityType.wms.warehouse_location": "Ubicación",
|
|
2
6
|
"wms.audit.inventory.receive": "Recibir inventario",
|
|
3
7
|
"wms.audit.salesOrder.assignWarehouse": "Asignar almacén al pedido",
|
|
4
8
|
"wms.audit.salesOrder.reRunReservation": "Volver a ejecutar la reserva del pedido",
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
+
"search.entityType.wms.inventory_lot": "재고 로트",
|
|
3
|
+
"search.entityType.wms.product_inventory_profile": "재고 프로필",
|
|
4
|
+
"search.entityType.wms.warehouse": "창고",
|
|
5
|
+
"search.entityType.wms.warehouse_location": "위치",
|
|
2
6
|
"wms.audit.inventory.receive": "재고 수령",
|
|
3
7
|
"wms.audit.salesOrder.assignWarehouse": "주문할 창고 지정",
|
|
4
8
|
"wms.audit.salesOrder.reRunReservation": "주문 예약 재실행",
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
+
"search.entityType.wms.inventory_lot": "Partia zapasu",
|
|
3
|
+
"search.entityType.wms.product_inventory_profile": "Profil zapasu",
|
|
4
|
+
"search.entityType.wms.warehouse": "Magazyn",
|
|
5
|
+
"search.entityType.wms.warehouse_location": "Lokalizacja",
|
|
2
6
|
"wms.audit.inventory.receive": "Przyjmij zapas",
|
|
3
7
|
"wms.audit.salesOrder.assignWarehouse": "Przypisz magazyn do zamówienia",
|
|
4
8
|
"wms.audit.salesOrder.reRunReservation": "Ponów rezerwację dla zamówienia",
|
|
@@ -63,7 +63,7 @@ function buildWarehousePresenter(
|
|
|
63
63
|
const label = t('wms.search.badge.warehouse', 'Warehouse')
|
|
64
64
|
const title = pickString(record.name, record.code, record.id) ?? label
|
|
65
65
|
const subtitle = formatSubtitle(record.code, record.city, record.country)
|
|
66
|
-
return { title, subtitle, icon: 'warehouse'
|
|
66
|
+
return { title, subtitle, icon: 'warehouse' }
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function buildLocationPresenter(
|
|
@@ -73,7 +73,7 @@ function buildLocationPresenter(
|
|
|
73
73
|
const label = t('wms.search.badge.location', 'Location')
|
|
74
74
|
const title = pickString(record.code, record.id) ?? label
|
|
75
75
|
const subtitle = formatSubtitle(record.type, record.warehouse_id ?? record.warehouseId)
|
|
76
|
-
return { title, subtitle, icon: 'map-pinned'
|
|
76
|
+
return { title, subtitle, icon: 'map-pinned' }
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function buildInventoryProfilePresenter(
|
|
@@ -89,7 +89,7 @@ function buildInventoryProfilePresenter(
|
|
|
89
89
|
record.default_uom ?? record.defaultUom,
|
|
90
90
|
variantId ? t('wms.search.profile.variantScoped', 'Variant-scoped') : t('wms.search.profile.productScoped', 'Product-scoped'),
|
|
91
91
|
)
|
|
92
|
-
return { title, subtitle, icon: 'boxes'
|
|
92
|
+
return { title, subtitle, icon: 'boxes' }
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function buildLotPresenter(
|
|
@@ -99,7 +99,7 @@ function buildLotPresenter(
|
|
|
99
99
|
const label = t('wms.search.badge.lot', 'Inventory lot')
|
|
100
100
|
const title = pickString(record.lot_number, record.lotNumber, record.batch_number, record.batchNumber, record.id) ?? label
|
|
101
101
|
const subtitle = formatSubtitle(record.catalog_variant_id ?? record.catalogVariantId, record.status, record.expires_at ?? record.expiresAt)
|
|
102
|
-
return { title, subtitle, icon: 'package-search'
|
|
102
|
+
return { title, subtitle, icon: 'package-search' }
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export const searchConfig: SearchModuleConfig = {
|