From 5a020cfafa8927d094eef10df16ac64842c0f6b4 Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Sat, 25 Apr 2026 22:49:42 +0500 Subject: [PATCH] =?UTF-8?q?feat(supply+products-list):=20=D1=87=D0=B5?= =?UTF-8?q?=D0=BA=D0=B1=D0=BE=D0=BA=D1=81=20=C2=AB=D0=9F=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D0=B4=D0=B5=D0=BD=D0=BE=C2=BB=20=D1=81=20confirm=20+=20?= =?UTF-8?q?=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D0=BD=D0=B0=D1=8F=20=D1=80?= =?UTF-8?q?=D0=BE=D0=B7=D0=BD=D0=B8=D1=87=D0=BD=D0=B0=D1=8F=20=D0=B2=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supply edit page: - Кнопки «Провести / Отменить проведение» заменены на чекбокс «Проведено» у заголовка. При попытке отметки — confirm «После проведения товары будут оприходованы на склад. Продолжить?», при снятии — confirm «Снять проведение? Остатки откатятся, себестоимость останется (пересчитать вручную при необходимости).». - Чекбокс disabled пока хотя бы одна строка пустая (Quantity ≤ 0 или UnitPrice ≤ 0) или вообще нет строк. - Backend (post/unpost) уже корректно делает StockMovement и пересчёт цен из Phase3a — UI просто переключает status. Products list: - Колонка «Эталонная цена» заменена на колонку системной розничной. Заголовок = Name той PriceType что IsSystem=true (если пользователь переименовал «Розничная цена» → «Продажная цена», заголовок колонки автоматически становится «Продажная цена»). - Значение = Product.Prices[ системного PriceType ].Amount. Если у товара нет такой записи — «—» (тире). - Подпись фильтра «Закупочная цена» → «Эталонная цена» (поведение фильтра по диапазону цены остаётся прежним). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/pages/ProductsPage.tsx | 31 +++++++++++------ .../src/pages/SupplyEditPage.tsx | 33 ++++++++++++------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/food-market.web/src/pages/ProductsPage.tsx b/src/food-market.web/src/pages/ProductsPage.tsx index 5353649..b9e9824 100644 --- a/src/food-market.web/src/pages/ProductsPage.tsx +++ b/src/food-market.web/src/pages/ProductsPage.tsx @@ -7,6 +7,7 @@ import { Button } from '@/components/Button' import { Plus, Filter, X, FolderTree } from 'lucide-react' import { useCatalogList } from '@/lib/useCatalog' import { useOrgSettings } from '@/lib/useOrgSettings' +import { usePriceTypes } from '@/lib/useLookups' import { ProductGroupTree } from '@/components/ProductGroupTree' import { MoneyInput } from '@/components/Field' import { packagingLabel, type Product } from '@/lib/types' @@ -103,6 +104,8 @@ export function ProductsPage() { const [filtersOpen, setFiltersOpen] = useState(false) const { data, isLoading, page, setPage, search, setSearch, sortKey, sortOrder, setSort } = useCatalogList(URL, toExtra(filters)) const org = useOrgSettings() + const priceTypes = usePriceTypes() + const systemPriceType = priceTypes.data?.find((pt) => pt.isSystem) ?? priceTypes.data?.find((pt) => pt.isDefault) ?? priceTypes.data?.[0] const showVat = org.data?.showVatEnabledOnProduct ?? false const showService = org.data?.showServiceOnProduct ?? false const showMarked = org.data?.showMarkedOnProduct ?? false @@ -127,15 +130,23 @@ export function ProductsPage() { { header: 'Штрихкод', width: '160px', cell: (r) => ( {r.barcodes[0]?.code ?? '—'} )}, - { header: 'Эталонная цена', width: '160px', className: 'text-right font-mono', sortKey: 'referencePrice', cell: (r) => { - if (r.referencePrice == null) return '—' - const fractional = org.data?.allowFractionalPrices ?? false - const num = r.referencePrice.toLocaleString('ru', - fractional - ? { minimumFractionDigits: 2, maximumFractionDigits: 2 } - : { maximumFractionDigits: 0 }) - return `${num} ${r.purchaseCurrencyCode ?? ''}`.trim() - }}, + // Колонка системной розничной цены: заголовок = PriceType.Name той записи + // что помечена IsSystem (если пользователь её переименовал — заголовок меняется). + { + header: systemPriceType?.name ?? 'Розничная цена', + width: '170px', + className: 'text-right font-mono', + cell: (r) => { + const pr = systemPriceType ? r.prices?.find(x => x.priceTypeId === systemPriceType.id) : undefined + if (!pr) return '—' + const fractional = org.data?.allowFractionalPrices ?? false + const num = pr.amount.toLocaleString('ru', + fractional + ? { minimumFractionDigits: 2, maximumFractionDigits: 2 } + : { maximumFractionDigits: 0 }) + return `${num} ${pr.currencyCode ?? ''}`.trim() + }, + }, ] if (showVat) { baseColumns.push({ header: 'НДС', width: '90px', className: 'text-right', sortKey: 'vat', cell: (r) => r.vatEnabled ? `${r.vat.toFixed(2)}%` : '—' }) @@ -223,7 +234,7 @@ export function ProductsPage() { { setFilters({ ...filters, isMarked: v }); setPage(1) }} /> )}
- Закупочная цена + Эталонная цена
-
- {isPosted && ( - +
+ {!isNew && ( + )} {isDraft && !isNew && ( )} {isDraft && ( - )} - {isDraft && !isNew && ( - - )}