diff --git a/src/food-market.web/src/components/Field.tsx b/src/food-market.web/src/components/Field.tsx index b6bf286..8c90034 100644 --- a/src/food-market.web/src/components/Field.tsx +++ b/src/food-market.web/src/components/Field.tsx @@ -1,5 +1,6 @@ -import type { InputHTMLAttributes, SelectHTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react' +import { useEffect, type InputHTMLAttributes, type SelectHTMLAttributes, type ReactNode, type TextareaHTMLAttributes } from 'react' import { cn } from '@/lib/utils' +import { useOrgSettings } from '@/lib/useOrgSettings' interface FieldProps { label: string @@ -38,7 +39,8 @@ interface MoneyInputProps { onChange: (v: number | null) => void currencyCode?: string | null currencySymbol?: string | null - /** Разрешать ли две цифры после запятой. По умолчанию false (целые). */ + /** Если задан — переопределяет настройку организации. Если не задан — + * читается из useOrgSettings().allowFractionalPrices. */ allowFractional?: boolean disabled?: boolean placeholder?: string @@ -47,34 +49,50 @@ interface MoneyInputProps { /** Денежное поле: только цифры (+ точка/запятая если allowFractional), * справа — символ валюты (₸/$/€). При allowFractional=false дробная часть - * отбрасывается на лету и отображается целым; иначе — два знака после - * запятой. Пустое поле → null. */ + * отбрасывается на лету и сразу синхронизируется в state наружу; иначе — + * две цифры после запятой. Пустое поле → null. */ export function MoneyInput({ - value, onChange, currencyCode, currencySymbol, allowFractional = false, + value, onChange, currencyCode, currencySymbol, allowFractional, disabled, placeholder = '0', className, }: MoneyInputProps) { + const org = useOrgSettings() + // Пока org.data не загружен и prop не задан — фактическое поведение + // оставляем «как сейчас в state», чтобы не резать дробь до приезда настройки. + const settingKnown = allowFractional !== undefined || org.data !== undefined + const fractional = allowFractional ?? org.data?.allowFractionalPrices ?? true + + // Когда настройка известна и она запрещает дробное, а в state лежит дробь — + // синхронизируем округлённое значение наружу. Это нужно чтобы при сохранении + // не уходило значение, которое юзер не видит на экране. + useEffect(() => { + if (!settingKnown || fractional) return + if (value == null) return + const rounded = Math.round(value) + if (rounded !== value) onChange(rounded) + }, [settingKnown, fractional, value, onChange]) + const suffix = currencySymbol || currencyCode || '₸' - const display = value == null ? '' : (allowFractional ? String(value) : String(Math.round(value))) + const display = value == null ? '' : (fractional ? String(value) : String(Math.round(value))) return (
e.currentTarget.select()} onChange={(e) => { let raw = e.target.value.replace(',', '.') - raw = allowFractional ? raw.replace(/[^\d.]/g, '') : raw.replace(/[^\d]/g, '') + raw = fractional ? raw.replace(/[^\d.]/g, '') : raw.replace(/[^\d]/g, '') if (raw === '') return onChange(null) - if (allowFractional) { + if (fractional) { const parts = raw.split('.') raw = parts.length > 1 ? parts[0] + '.' + parts.slice(1).join('') : raw } const n = Number(raw) if (!Number.isFinite(n)) return - onChange(allowFractional ? n : Math.round(n)) + onChange(fractional ? n : Math.round(n)) }} className={cn(inputClass, 'pr-10 text-right tabular-nums')} /> diff --git a/src/food-market.web/src/pages/ProductEditPage.tsx b/src/food-market.web/src/pages/ProductEditPage.tsx index 54015a8..33b0090 100644 --- a/src/food-market.web/src/pages/ProductEditPage.tsx +++ b/src/food-market.web/src/pages/ProductEditPage.tsx @@ -323,7 +323,7 @@ export function ProductEditPage() { onChange={(n) => setForm({ ...form, purchasePrice: n == null ? '' : String(n) })} currencyCode={currencies.data?.find((c) => c.id === form.purchaseCurrencyId)?.code ?? org.data?.defaultCurrencyCode ?? undefined} currencySymbol={currencies.data?.find((c) => c.id === form.purchaseCurrencyId)?.symbol ?? org.data?.defaultCurrencySymbol ?? undefined} - allowFractional={org.data?.allowFractionalPrices ?? false} + /> {org.data?.multiCurrencyEnabled && ( @@ -379,7 +379,7 @@ export function ProductEditPage() { onChange={(n) => updatePrice(i, { amount: n ?? 0 })} currencyCode={currencies.data?.find((c) => c.id === p.currencyId)?.code ?? org.data?.defaultCurrencyCode ?? undefined} currencySymbol={currencies.data?.find((c) => c.id === p.currencyId)?.symbol ?? org.data?.defaultCurrencySymbol ?? undefined} - allowFractional={org.data?.allowFractionalPrices ?? false} + />
{org.data?.multiCurrencyEnabled && ( diff --git a/src/food-market.web/src/pages/ProductsPage.tsx b/src/food-market.web/src/pages/ProductsPage.tsx index fbbfe48..1f9520c 100644 --- a/src/food-market.web/src/pages/ProductsPage.tsx +++ b/src/food-market.web/src/pages/ProductsPage.tsx @@ -230,7 +230,7 @@ export function ProductsPage() { onChange={(n) => { setFilters({ ...filters, purchasePriceFrom: n }); setPage(1) }} currencyCode={org.data?.defaultCurrencyCode ?? undefined} currencySymbol={org.data?.defaultCurrencySymbol ?? undefined} - allowFractional={org.data?.allowFractionalPrices ?? false} + placeholder="от" /> @@ -240,7 +240,7 @@ export function ProductsPage() { onChange={(n) => { setFilters({ ...filters, purchasePriceTo: n }); setPage(1) }} currencyCode={org.data?.defaultCurrencyCode ?? undefined} currencySymbol={org.data?.defaultCurrencySymbol ?? undefined} - allowFractional={org.data?.allowFractionalPrices ?? false} + placeholder="до" /> diff --git a/src/food-market.web/src/pages/RetailSaleEditPage.tsx b/src/food-market.web/src/pages/RetailSaleEditPage.tsx index 2007998..671cb39 100644 --- a/src/food-market.web/src/pages/RetailSaleEditPage.tsx +++ b/src/food-market.web/src/pages/RetailSaleEditPage.tsx @@ -289,14 +289,14 @@ export function RetailSaleEditPage() { onChange={(n) => setForm({ ...form, paidCash: n ?? 0 })} currencyCode={currencies.data?.find((c) => c.id === form.currencyId)?.code} currencySymbol={currencies.data?.find((c) => c.id === form.currencyId)?.symbol} - allowFractional={org.data?.allowFractionalPrices ?? false} /> + /> setForm({ ...form, paidCard: n ?? 0 })} currencyCode={currencies.data?.find((c) => c.id === form.currencyId)?.code} currencySymbol={currencies.data?.find((c) => c.id === form.currencyId)?.symbol} - allowFractional={org.data?.allowFractionalPrices ?? false} /> + />