From d271cd7410f645d4101c1f7f0ed8fb511669d399 Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:53:49 +0500 Subject: [PATCH] =?UTF-8?q?refactor(retail-points):=20rename=20=C2=AB?= =?UTF-8?q?=D0=A2=D0=BE=D1=87=D0=BA=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=D0=B6=C2=BB=20=E2=86=92=20=C2=AB=D0=9A=D0=B0=D1=81=D1=81=D0=B0?= =?UTF-8?q?=C2=BB=20+=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=D1=81=20?= =?UTF-8?q?=D1=81=D0=BA=D0=BB=D0=B0=D0=B4=D0=BE=D0=B2=20=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=B0=D1=81=D1=81=20=D0=B2=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=20=C2=AB=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BE=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=C2=BB;=20useOrgInfra=20=D1=85=D1=83=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI-переименование: - RetailPointsPage: title «Кассы», description обновлён, лейблы «Новая касса» / «Удалить кассу?»; доменная сущность RetailPoint и URL /api/catalog/retail-points сохранены — DTO/БД не трогаем. - В сайдбаре пункты «Склады» и «Кассы» перенесены из бывшей группы «Склады» в группу «Настройки организации» (рядом с «Общие»). Старые пункты верхнего уровня убраны. useOrgInfra() — общий хук: - возвращает stores, cashRegisters, defaultStoreId, defaultCashId - showStorePicker / showCashPicker = length > 1 (умное скрытие селекторов в формах документов когда инфра одна). В SupplyEditPage скрытие склада уже работало через (stores.data?.length ?? 0) > 1 — оставил как есть, новый хук для будущих документов (продажи, инвентаризации). Сидер default Store + RetailPoint per Organization уже есть в DevDataSeeder.cs (Основной склад MAIN + Касса 1 POS-1) — дополнять не нужно. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/components/AppLayout.tsx | 10 +++--- src/food-market.web/src/lib/useOrgInfra.ts | 34 +++++++++++++++++++ .../src/pages/RetailPointsPage.tsx | 8 ++--- 3 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 src/food-market.web/src/lib/useOrgInfra.ts diff --git a/src/food-market.web/src/components/AppLayout.tsx b/src/food-market.web/src/components/AppLayout.tsx index 643a947..490c683 100644 --- a/src/food-market.web/src/components/AppLayout.tsx +++ b/src/food-market.web/src/components/AppLayout.tsx @@ -37,10 +37,6 @@ function buildNav(): NavSection[] { { group: 'Контрагенты', items: [ { to: '/catalog/counterparties', icon: Users, label: 'Контрагенты' }, ]}, - { group: 'Склады', items: [ - { to: '/catalog/stores', icon: Warehouse, label: 'Склады' }, - { to: '/catalog/retail-points', icon: StoreIcon, label: 'Точки продаж' }, - ]}, { group: 'Остатки', items: [ { to: '/inventory/stock', icon: Boxes, label: 'Остатки' }, { to: '/inventory/movements', icon: History, label: 'Движения' }, @@ -57,8 +53,10 @@ function buildNav(): NavSection[] { { group: 'Импорт', items: [ { to: '/admin/import/moysklad', icon: Download, label: 'МойСклад' }, ]}, - { group: 'Настройки', items: [ - { to: '/settings/organization', icon: Settings, label: 'Организация' }, + { group: 'Настройки организации', items: [ + { to: '/settings/organization', icon: Settings, label: 'Общие' }, + { to: '/catalog/stores', icon: Warehouse, label: 'Склады' }, + { to: '/catalog/retail-points', icon: StoreIcon, label: 'Кассы' }, ]}, ] } diff --git a/src/food-market.web/src/lib/useOrgInfra.ts b/src/food-market.web/src/lib/useOrgInfra.ts new file mode 100644 index 0000000..51175d6 --- /dev/null +++ b/src/food-market.web/src/lib/useOrgInfra.ts @@ -0,0 +1,34 @@ +import { useStores } from '@/lib/useLookups' +import { useQuery } from '@tanstack/react-query' +import { api } from '@/lib/api' +import type { PagedResult, RetailPoint } from '@/lib/types' + +/** Инфраструктура организации (склады, кассы) для умного скрытия селекторов + * в формах документов: если у организации только 1 склад/касса — выбор не + * показывается, форма автоматически подставляет единственное значение. + * Появится 2-й — пикер вернётся. */ +export function useOrgInfra() { + const stores = useStores() + const cashRegisters = useQuery({ + queryKey: ['lookup:retail-points'], + queryFn: async () => + (await api.get>('/api/catalog/retail-points?pageSize=500')).data.items, + staleTime: 0, + refetchOnMount: 'always', + refetchOnWindowFocus: true, + }) + + const storesList = stores.data ?? [] + const cashList = cashRegisters.data ?? [] + const mainStore = storesList.find((s) => s.isMain) ?? storesList[0] + + return { + stores: storesList, + cashRegisters: cashList, + isLoading: stores.isLoading || cashRegisters.isLoading, + showStorePicker: storesList.length > 1, + showCashPicker: cashList.length > 1, + defaultStoreId: mainStore?.id ?? null, + defaultCashId: cashList[0]?.id ?? null, + } +} diff --git a/src/food-market.web/src/pages/RetailPointsPage.tsx b/src/food-market.web/src/pages/RetailPointsPage.tsx index 4f4a45e..38ed5b3 100644 --- a/src/food-market.web/src/pages/RetailPointsPage.tsx +++ b/src/food-market.web/src/pages/RetailPointsPage.tsx @@ -55,8 +55,8 @@ export function RetailPointsPage() { return ( <> @@ -96,12 +96,12 @@ export function RetailPointsPage() { setForm(null)} - title={form?.id ? 'Редактировать точку продаж' : 'Новая точка продаж'} + title={form?.id ? 'Редактировать кассу' : 'Новая касса'} footer={ <> {form?.id && (