From 748abf7eff80e1279f5d9756ddf47a0696326ae5 Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Sat, 25 Apr 2026 22:55:56 +0500 Subject: [PATCH] =?UTF-8?q?feat(product-prices):=20inputs=20=D0=BF=D0=BE?= =?UTF-8?q?=20=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D1=87=D0=BD=D0=B8=D0=BA?= =?UTF-8?q?=D1=83=20PriceType=20=E2=80=94=20=D0=B1=D0=B5=D0=B7=20dropdown'?= =?UTF-8?q?a?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Раньше каждая цена в карточке товара рендерилась как dropdown «выбор PriceType» + поле ввода + кнопка удаления. Это было избыточно: типы цен и так фиксированы справочником, выбирать нечего. Теперь: - Идём по справочнику PriceType (отсортирован по SortOrder→Name). - На каждый PriceType — одна строка: label = pt.Name, поле MoneyInput. - IsRequired запись помечается красной звёздочкой * после имени. - Стерев значение — строка убирается из form.prices (UI пусто). - Введя значение — создаётся новая запись (currency = KZT fallback из справочника), либо обновляется существующая. - Кнопка «+ Добавить» и иконка удаления убраны — управление набором типов цен теперь только через «Настройки → Типы цен». addPrice/removePrice вспомогательные функции удалены за ненадобностью. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/pages/ProductEditPage.tsx | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/src/food-market.web/src/pages/ProductEditPage.tsx b/src/food-market.web/src/pages/ProductEditPage.tsx index b54ef22..a00265b 100644 --- a/src/food-market.web/src/pages/ProductEditPage.tsx +++ b/src/food-market.web/src/pages/ProductEditPage.tsx @@ -179,12 +179,6 @@ export function ProductEditPage() { const onSubmit = (e: FormEvent) => { e.preventDefault(); save.mutate() } - const addPrice = () => setForm({ ...form, prices: [...form.prices, { - priceTypeId: priceTypes.data?.find(p => !form.prices.some(x => x.priceTypeId === p.id))?.id ?? priceTypes.data?.[0]?.id ?? '', - amount: 0, - currencyId: currencies.data?.find(c => c.code === 'KZT')?.id ?? '', - }] }) - const removePrice = (i: number) => setForm({ ...form, prices: form.prices.filter((_, ix) => ix !== i) }) const updatePrice = (i: number, patch: Partial) => setForm({ ...form, prices: form.prices.map((p, ix) => ix === i ? { ...p, ...patch } : p) }) @@ -409,49 +403,51 @@ export function ProductEditPage() { Привести к себестоимости )} - } > - {form.prices.length === 0 ? ( -
Цен ещё нет. Добавь хотя бы розничную.
- ) : ( -
- {form.prices.map((p, i) => ( -
-
- -
-
+ {/* Список цен рендерится по справочнику PriceType: одно поле на каждый + * тип, без выпадашки выбора. Значение хранится в form.prices, + * key = priceTypeId. Для отсутствующих записей при наборе создаётся + * новая, при стирании — null Amount (UI пустое). Системная запись + * (IsSystem) и обязательные (IsRequired) помечаются звёздочкой. */} +
+ {priceTypes.data?.slice().sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name)).map((pt) => { + const idx = form.prices.findIndex(p => p.priceTypeId === pt.id) + const row = idx >= 0 ? form.prices[idx] : null + const required = pt.isRequired + return ( +
+ +
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} - + value={row?.amount ?? null} + onChange={(n) => { + if (n == null) { + // Стерли значение — удаляем строку, чтобы не слать 0 как required. + setForm({ ...form, prices: form.prices.filter(x => x.priceTypeId !== pt.id) }) + return + } + if (idx >= 0) { + updatePrice(idx, { amount: n }) + } else { + const cur = currencies.data?.find(c => c.code === 'KZT')?.id ?? currencies.data?.[0]?.id ?? '' + setForm({ ...form, prices: [...form.prices, { priceTypeId: pt.id, amount: n, currencyId: cur }] }) + } + }} + currencyCode={currencies.data?.find((c) => c.id === row?.currencyId)?.code ?? org.data?.defaultCurrencyCode ?? undefined} + currencySymbol={currencies.data?.find((c) => c.id === row?.currencyId)?.symbol ?? org.data?.defaultCurrencySymbol ?? undefined} />
- {org.data?.multiCurrencyEnabled && ( -
- -
- )} -
- ))} -
- )} + ) + })} + {priceTypes.data?.length === 0 && ( +
Нет ни одного типа цен. Создай в «Настройки → Типы цен».
+ )} +
{!isNew && id && (