16 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b69ba4950b |
feat(product-card): drop ShelfLifeDays + recompose classification + auto-article + barcode trash hide
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 46s
CI / Web (React + Vite) (push) Successful in 35s
Docker API / Build + push API (push) Successful in 44s
Docker Web / Build + push Web (push) Successful in 25s
Docker API / Deploy API on stage (push) Successful in 17s
Docker Web / Deploy Web on stage (push) Successful in 11s
- Удаление поля «Срок годности (дней)»:
• Domain.Product.ShelfLifeDays убран,
• миграция Phase3b_DropProductShelfLifeDays — DROP COLUMN,
• DTO/Input/UI/фильтр в списке товаров — выпилены.
- Перекомпоновка секции «Классификация» в карточке товара:
• ряд 1 (3 col): Группа * | Единица измерения * | Фасовка,
• ряд 2 (2 col): Основной поставщик | Страна происхождения,
• Страна происхождения видна только если включена настройка
Organization.ShowCountryOfOriginOnProduct (default false).
• Та же миграция добавляет колонку, в OrganizationSettingsPage
появляется галка «Показывать «Страну происхождения» на товаре»
с подсказкой про импорт.
- Артикул теперь обязательное поле с авто-генерацией:
• ProductEditPage: метка «Артикул *», required,
• генератор generateArticle() (timestamp[-6] + 3 random) — у нового
товара поле сразу заполнено,
• canSave требует непустой article. Уникальность подтверждает
сервер (он также имеет свой fallback-генератор max+1).
- Иконка корзины в секции «Штрихкоды» рендерится только при
form.barcodes.length > 1 — для единственной строки удаления нет
(минимум 1 штрихкод обязателен, удалять единственный нельзя).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
bcc6976bd0 |
chore(price-types): drop IsDefault flag + rename IsRetail label + uniqueness
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 46s
CI / Web (React + Vite) (push) Successful in 34s
Docker API / Build + push API (push) Successful in 40s
Docker Web / Build + push Web (push) Successful in 27s
Docker API / Deploy API on stage (push) Successful in 17s
Docker Web / Deploy Web on stage (push) Successful in 11s
PriceType: убран флаг IsDefault — он семантически дублировал IsSystem
(защищённая запись «по умолчанию»). Остаются IsSystem / IsRequired /
IsRetail.
- Domain.Catalog.PriceType: удалено поле IsDefault.
- Миграция Phase3b_DropPriceTypeIsDefault: DROP COLUMN.
- DTO/Input (PriceTypeDto, PriceTypeInput) — без IsDefault.
- PriceTypesController:
• убрана логика uniqueness IsDefault на Create/Update,
• IsRetail теперь enforce'ит уникальность: при установке IsRetail=true
у других записей сбрасывается,
• при удалении единственной IsRetail записи (если она не системная)
IsRetail автоматически переезжает на IsSystem-запись — у организации
всегда остаётся один POS-кандидат.
- ProductsController.RecalcRetail и SuppliesController.SetDefaultRetail —
поиск дефолтной розничной идёт по IsSystem → IsRetail → SortOrder → Name
(ранее ThenByDescending(IsDefault) — выпилено).
- DevDataSeeder: поле IsDefault убрано.
- web types.ts: убрано isDefault из PriceType.
- PriceTypesPage:
• убран чекбокс «По умолчанию»,
• лейбл «Розничная (используется на кассе)» → «Используется на кассе»,
• Form/blankForm/onRowClick без isDefault.
- ProductsPage / ProductEditPage: фоллбэк дефолтной цены теперь
IsSystem → IsRetail → первая.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
3c274541e9 |
feat(phase3b): drop IsActive, add ShelfLifeDays, restore PriceType IsSystem/IsRequired
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 46s
CI / Web (React + Vite) (push) Successful in 34s
Docker API / Build + push API (push) Successful in 41s
Docker Web / Build + push Web (push) Successful in 25s
Docker API / Deploy API on stage (push) Successful in 12s
Docker Web / Deploy Web on stage (push) Successful in 11s
Domain + миграция Phase3b_PricingCleanup: - DROP IsActive у products / product_groups / units_of_measure / counterparties / price_types (включая индекс IX_products_OrganizationId_IsActive). В этих сущностях концепт деактивации не оправдан — если товар/группа/единица/контрагент не нужны, их физически удаляют. - DROP organizations.MultiplePriceTypesEnabled — раздел «Типы цен» всегда виден, отдельной настройки больше не нужно. - ADD price_types.IsRequired bool default false — обязательность заполнения для каждого товара. - ADD price_types.IsSystem bool default false — защищённая запись, не удаляется и IsRequired всегда true; имя редактируется. В каждой организации гарантируется одна системная запись «Розничная цена» (создаётся миграцией если её нет). - ADD products.ShelfLifeDays integer NULL — срок годности. API: - ProductsController/UnitsOfMeasureController/ProductGroupsController/ CounterpartiesController/PriceTypesController: убраны параметры isActive в фильтрах, sort-keys, DTO, Apply, Создании. - Products проекция: вместо IsActive теперь ShelfLifeDays. - PriceTypesController: 400 при попытке удалить системную запись; IsRequired у системной — всегда true, не меняется через PUT. - recalc-retail / supply posting: дефолтный PriceType ищется по IsSystem → IsDefault → IsRetail → SortOrder → Name (без IsActive). - OrgSettingsDto/Input — без MultiplePriceTypesEnabled. Web: - types.ts: убраны isActive у Product/ProductGroup/UnitOfMeasure/ Counterparty/PriceType. PriceType пополнен isRequired/isSystem. Product получил shelfLifeDays. - useOrgSettings: убрано multiplePriceTypesEnabled. - AppLayout: меню «Типы цен» всегда видно. - Pages (Counterparties/Units/ProductGroups/PriceTypes/ProductEdit/ OrganizationSettings): сняты колонки/чекбоксы/поля «Активен»; удалён GroupMarkupsPage; в PriceTypesPage добавлен Lock-индикатор системной записи и блок-подсказка, кнопка удаления скрыта. - DemoCatalogSeeder и MoySklad-импортёр: больше не пишут IsActive. UI-перекомпоновка карточки товара (Phase3b пп.6/9), Supply Posted-toggle, PercentInput, ShelfLifeDays-фильтр и редизайн прайс-секции — отдельными коммитами далее по плану. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
23d6f2bd5a |
feat(domain): pricing model rename and new fields (Phase3a)
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 44s
CI / Web (React + Vite) (push) Successful in 34s
Docker API / Build + push API (push) Successful in 41s
Docker Web / Build + push Web (push) Successful in 28s
Docker API / Deploy API on stage (push) Successful in 17s
Docker Web / Deploy Web on stage (push) Successful in 11s
Подготовка к новой модели цен МойСклад-style: - Product.PurchasePrice → ReferencePrice (справочная закупочная, не обязательная). + ReferencePriceUpdatedAt для 30-дневного таймера. - Product.+ Cost numeric(18,4) — себестоимость по скользящему среднему. - Product.+ LastSupplyAt — UTC последней Posted приёмки. - ProductGroup.+ MarkupPercent (5,2) — % наценки на cost для авто-розничной. - Organization.+ MultiplePriceTypesEnabled (default false) и ShowReferencePriceOnProduct (default true). - SupplyLine.+ RetailPriceManuallyOverridden + RetailPriceOverride — отметка ручной правки розничной в строке приёмки. Миграция Phase3a_PricingModel: RENAME + AddColumn'ы. Logic перерасчёта себестоимости, автонаценки, recalc-endpoint и Hangfire job — следующими коммитами. DTO/контроллеры/MoySklad-импорт/UI поля переименованы в referencePrice (включая фильтры списка товаров). UI-логика следующего коммита будет показывать Cost и кнопку «привести розничную к себестоимости»; пока referencePrice работает как раньше. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c2fa47c341 |
feat(product): группа обязательна, ≥1 штрихкод, умные дефолты на новом
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 26s
CI / Web (React + Vite) (push) Successful in 25s
Docker Images / API image (push) Successful in 38s
Docker Images / Web image (push) Successful in 26s
Docker Images / Deploy stage (push) Successful in 18s
- Product.ProductGroupId теперь NOT NULL (Guid вместо Guid?). Миграция Phase5g_RequiredProductGroup делает backfill: создаёт «Продукты питания» в каждой организации, у которой есть товары без группы, переносит туда null-значения, потом ALTER COLUMN NOT NULL. - ProductDto/ProductInput: ProductGroupId/Name без `?`. - ProductsController.Create/Update: 400 если barcodes пустой. - MoySklad-импорт: при отсутствии productFolder у товара ставится defaultGroupId — id «Продукты питания» (создаётся при необходимости). - DemoCatalogSeeder: «Продукты питания» добавлена в seed-набор групп. - ProductEditPage: • новый товар сразу получает 1 EAN-13 в barcodes, • Single-select Единица измерения и Группа лишились опции «—», • дефолт unitOfMeasureId — id единицы code='796' (штука), • дефолт productGroupId — «Продукты питания» (или первая), • Save disabled пока имя/единица/группа/≥1 штрихкод не заполнены, • если штрихкоды удалены — красная подсказка вместо нейтральной. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4d19015d6d |
feat(forms): MoneyInput/NumberInput + select-пагинация + Range на бэкенде
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 28s
CI / Web (React + Vite) (push) Successful in 23s
Docker Images / API image (push) Successful in 39s
Docker Images / Web image (push) Successful in 26s
Docker Images / Deploy stage (push) Successful in 18s
UI: - Pagination: ввод страницы заменён на select (option 1..totalPages), по выбору сразу setPage. Стрелки ← → остаются. - Field.tsx: добавлены MoneyInput (decimal + суффикс ₸/$/€) и NumberInput (decimal без валюты). Оба фильтруют ввод регулярно (только цифры + точка/запятая→точка), при focus — выделяют значение. - ProductEditPage: purchasePrice / vat / minStock / maxStock / amount в ценах продаж переведены на новые компоненты; символ валюты — из выбранной валюты позиции/закупки или из defaultCurrencySymbol орг. - SupplyEditPage / RetailSaleEditPage: quantity/unitPrice/discount в строках, paidCash/paidCard в шапке — на NumberInput/MoneyInput с символом из form.currencyId. - CountriesPage: vatRate — NumberInput. API: - ProductInput / ProductPriceInput / SupplyLineInput / RetailSaleLineInput / RetailSaleInput — добавлены [Range(0,1e10)] на денежные/количественные поля и [Range(0,100)] на проценты. ASP.NET автоматически валидирует и возвращает 400 при выходе. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d6dcc75aa0 |
refactor(currencies): убрать IsActive и MinorUnit из UI/API
- Currency.IsActive удалён полностью (domain/DTO/API/web/миграция). Валюты — глобальный справочник; «архивировать» USD глобально бессмысленно, а per-tenant видимости у валют нет. - MinorUnit остаётся в БД (нужен для форматирования цен), но скрыт из UI: убран CurrencyDto.MinorUnit, CurrencyInput.MinorUnit, колонка «Знаки» из списка. - Форма валюты — 3 поля: Код / Название / Символ. - Миграция Phase5e_DropCurrencyIsActive дропает колонку. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3ed6fe25be |
refactor(vat): Product.Vat как decimal(5,2), поле видно только при VatEnabled
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 29s
CI / Web (React + Vite) (push) Successful in 22s
Docker Images / API image (push) Successful in 38s
Docker Images / Web image (push) Successful in 23s
Docker Images / Deploy stage (push) Successful in 17s
Единственная роль галки «В том числе НДС» на товаре — показать/скрыть поле «Ставка НДС %». Никакой семантики «в том числе/сверху» на товаре не живёт — это логика документа (продажи/поставки). - Product.Vat: int → decimal (миграция Phase5d_ProductVatDecimal меняет тип колонки на numeric(5,2)). - ProductDto/ProductInput: decimal? Vat. - ResolveDefaultVatAsync, seeders, MoySklad import — decimal. - MoySklad import: если vatEnabled пришёл — уважаем, иначе прежний fallback «vat=0 → без НДС». - UI: вместо жёсткого Select [0,10,12,16,20] — TextInput number step=0.01; поле рендерится только когда form.vatEnabled=true; дефолт для нового товара подставляется из Country.VatRate организации. - В таблице товаров ставка печатается с 2 знаками (16.00%). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
6cd9e27553 |
feat(tables): server-side sort by column header click
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 26s
CI / Web (React + Vite) (push) Successful in 22s
Docker Images / API image (push) Successful in 38s
Docker Images / Web image (push) Successful in 26s
Docker Images / Deploy stage (push) Successful in 18s
Во всех таблицах можно сортировать по клику на заголовок столбца:
первый клик — по возрастанию (↑), второй — по убыванию (↓),
смена колонки сбрасывает предыдущую. Без активной сортировки —
серверный default (обычно по Name ASC).
Реализация:
- PagedRequest: добавлены Sort (ключ колонки) и Order ("asc"/"desc"),
плюс удобное свойство Desc.
- DataTable: Column.sortKey + props sortKey/sortOrder/onSortChange,
в заголовке появляется иконка (ArrowUpDown/ArrowUp/ArrowDown).
- useCatalogList: хранит sortKey/sortOrder, отдаёт setSort, шлёт
?sort=&order= в query-string.
- Все 10 List-эндпоинтов (Countries, Currencies, UnitsOfMeasure,
PriceTypes, Stores, RetailPoints, Counterparties, ProductGroups,
Products, Supplies, RetailSales + Stock/Movements) принимают
параметры и применяют switch-based OrderBy по whitelisted ключам.
- Все страницы со списками прокидывают sort state и sortKey на
колонках, где сортировка имеет смысл (тексты/числа/даты).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4f4df4a715 |
refactor(countries): drop SortOrder, sort by Name, auto-width columns
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 27s
CI / Web (React + Vite) (push) Successful in 24s
Docker Images / API image (push) Successful in 40s
Docker Images / Web image (push) Successful in 25s
Docker Images / Deploy stage (push) Successful in 18s
- Country.SortOrder удалено из домена/DTO/API/seeder/web/UI. - Миграция Phase5b_DropCountrySortOrder дропает колонку. - Список стран сортируется по Name ASC. - В форме: поле «Порядок» убрано. - В таблице: убрана колонка «Порядок», ширины колонок сжаты по содержимому (Код 80px, Валюта 120px, НДС 100px, Название flex). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9d8c386def |
feat(vat): ставка в стране + опц. переопределение на товаре
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 27s
CI / Web (React + Vite) (push) Successful in 23s
Docker Images / API image (push) Successful in 38s
Docker Images / Web image (push) Successful in 25s
Docker Images / Deploy stage (push) Successful in 18s
Phase5_VatAsCountryProperty: - countries.VatRate (numeric(5,2)) — ставка страны, источник правды. Seed: KZ=16, RU=20, BY=20, DE=19, CN=13, TR=18, UZ=12, KG=12, KR=10, IT=22, PL=23, US=0. - organizations.ShowVatEnabledOnProduct (bool, default false) — флаг отображения на карточке товара. - organizations.DefaultVat удалён (заменён страной). - products.Vat ОСТАЁТСЯ: для KZ есть льготные категории (хлеб/молоко = 0%) и фискальный чек требует ставку на каждой позиции. Country domain: + DefaultCurrency / VatRate (уже было DefaultCurrencyId из Phase4, сейчас дополнено). Organization domain: DefaultVat убран, ShowVatEnabledOnProduct добавлен. Backend: - ProductInput.Vat теперь int? — если UI скрывает поле и прислал null, ProductsController берёт дефолт из страны организации (Country.VatRate при создании; при update сохраняет прежнее значение). - CountriesController.List/Get/Create/Update возвращает/принимает DefaultCurrency и VatRate. - MoySklad импорт: дефолт Vat загружается из страны организации. - SystemReferenceSeeder: новые валюты BYN/UZS/KGS/TRY/KRW/PLN, seed country-currency-vat для всех 12 стран. - OrganizationSettingsController: VatRate read-only из страны, ShowVatEnabledOnProduct редактируется. Web: - Country type + CountriesPage форма редактирования (валюта, ставка НДС). - OrganizationSettingsPage: "Ставка НДС" read-only (берётся из страны, ссылка на /catalog/countries), галочка "Указывать ставку НДС на товаре". - ProductEditPage: блок Ставка НДС % + галка "В том числе НДС" теперь показываются только если showVatEnabledOnProduct=true. В payload при save.mutate отправляется vat=null если скрыто. - ProductsPage: колонка НДС показывается только при включённом флаге. Galleries/products/settings других этапов — не задеты. |
||
|
|
414d185765 |
feat(product): enum Packaging (штучный/весовой/разливной) вместо IsWeighed
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 30s
CI / Web (React + Vite) (push) Successful in 21s
Docker Images / API image (push) Successful in 46s
Docker Images / Web image (push) Successful in 23s
Docker Images / Deploy stage (push) Successful in 17s
Миграция Phase4b_ProductPackaging: products.IsWeighed (bool) → products.Packaging (int enum) 1=Piece (default), 2=Weight, 3=Liquid Backfill: прежние весовые товары → Weight. Domain/DTO/Input/Controller/Seeder/MoySkladImport — всё обновлено. Web: - Packaging enum в types.ts. - ProductEditPage: select "Фасовка" вместо checkbox "Весовой". - Подпись чекбокса НДС уточнена: "НДС применяется (ставка выше)" — ссылается на поле Vat на товаре. - Удалён IsMarked checkbox текст → "Маркируемый (Честный знак / Datamatrix)". - ProductsPage фильтр: select Packaging вместо Tri(IsWeighed). |
||
|
|
8fc9ef1a2e |
feat: strict MoySklad schema — реплика потерянного f7087e9
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 27s
CI / Web (React + Vite) (push) Successful in 23s
Docker Images / API image (push) Successful in 34s
Docker Images / Web image (push) Successful in 27s
Docker Images / Deploy stage (push) Successful in 15s
Main расходился с БД стейджа (Phase2c3_MsStrict в history, но код ещё ссылался на VatRate etc.) — деплой ломался. Реплицирую удаление сущностей вручную, чтобы код совпадал с таблицами. Убрано (нет в MoySklad — не выдумываем): - Domain: VatRate сущность целиком; Counterparty.Kind + enum CounterpartyKind; Store.Kind + enum StoreKind; Product.IsAlcohol; UnitOfMeasure.Symbol/DecimalPlaces/IsBase. - EF: DbSet<VatRate>, ConfigureVatRate, Product.VatRate navigation, индекс Counterparty.Kind. - DTO/Input: соответствующие поля и VatRateDto/Input. - API: VatRatesController удалён; references в Products/Counterparties/Stores/UoM/Supplies/Retail/Stock. Добавлено как в MoySklad: - Product.Vat (int) + Product.VatEnabled — MoySklad держит НДС числом на товаре. - KZ default VAT 16% — applied в сидерах и в MoySkladImportService когда товар не принёс свой vat. MoySkladImportService: - ResolveKind убран; CompanyType=entrepreneur→Individual (как и было). - VatRates lookup → прямой p.Vat ?? 16 + p.Vat > 0 для VatEnabled. - baseUnit ищется по code="796" вместо IsBase. Web: - types.ts: убраны CounterpartyKind/StoreKind/VatRate/Product.vatRateId/vatPercent/isAlcohol/UoM.symbol/decimalPlaces/isBase; добавлено Product.vat/vatEnabled; унифицировано unitSymbol→unitName. - VatRatesPage удалён, роут из App.tsx тоже. - CounterpartiesPage/StoresPage/UnitsOfMeasurePage: убраны соответствующие поля в формах. - ProductEditPage: select "Ставка НДС" теперь с фиксированными 0/10/12/16/20 + чекбокс VatEnabled. - Stock/RetailSale/Supply pages: unitSymbol → unitName. deploy-stage unguarded — теперь код соответствует DB, авто-deploy безопасен. |
||
|
|
50e3676d71 |
phase2a: stock foundation (Stock + StockMovement) + MoySklad counterparty import
Domain:
- foodmarket.Domain.Inventory.Stock — materialized aggregate per (Product, Store)
with Quantity, ReservedQuantity, computed Available. Unique index on tenant+
product+store.
- foodmarket.Domain.Inventory.StockMovement — append-only journal with signed
quantity, optional UnitCost, MovementType enum (Initial, Supply, RetailSale,
WholesaleSale, CustomerReturn, SupplierReturn, TransferOut, TransferIn,
WriteOff, Enter, InventoryAdjustment), document linkage (type, id, number),
OccurredAt, CreatedBy, Notes.
Application:
- IStockService.ApplyMovementAsync draft — appends movement row + upserts
materialized Stock row in the same unit of work. Callers control SaveChanges
so a posting doc can bundle all lines atomically.
Infrastructure:
- StockService implementation over AppDbContext.
- InventoryConfigurations EF mapping (precision 18,4 on quantities/costs;
indexes for product+time, store+time, document lookup).
- Migration Phase2a_Stock applied to dev DB (tables stocks, stock_movements).
API (GET, read-only for now):
- /api/inventory/stock — filter by store, product, includeZero; joins product +
unit + store names; server-side pagination.
- /api/inventory/movements — journal filtered by store/product/date range;
movement type as string enum for UI labels.
- Both [Authorize] (any authenticated user).
MoySklad:
- MsCounterparty DTO (name, legalTitle, inn, kpp, companyType, tags...).
- MoySkladClient.StreamCounterpartiesAsync — paginated like products.
- MoySkladImportService.ImportCounterpartiesAsync — maps tags → Kind (supplier /
customer / both), companyType → LegalEntity/Individual; dedup by Name;
defensive trim on all string fields; per-item try/catch; batches of 500.
- /api/admin/moysklad/import-counterparties endpoint (Admin policy).
Web:
- /inventory/stock list page (store filter, include-zero toggle, search; shows
quantity/reserved/available with red-on-negative, grey-on-zero accents).
- /inventory/movements list page (store filter; colored quantity +/-, Russian
labels for each movement type).
- MoySklad import page restructured: single token test + two import buttons
(Товары, Контрагенты) + reusable ImportResult panel that handles both.
- Sidebar: new "Остатки" group with Остатки + Движения; icons Boxes + History.
Uses the ListPageShell pattern introduced in
|
||
|
|
6b86106937 |
phase1b: catalog CRUD API (countries, currencies, vat, units, stores, retail points, product groups, counterparties, products)
Application layer: - PagedRequest/PagedResult<T> with sane defaults (pageSize 50, max 500) - CatalogDtos: read DTOs with joined names + input DTOs for upsert - Product input supports nested Prices[] and Barcodes[] for atomic save API controllers (api/catalog/…): - countries, currencies (global, write requires SuperAdmin) - vat-rates, units-of-measure, price-types, stores (write: Admin/Manager) - retail-points (references Store, Admin/Manager write) - product-groups: hierarchy with auto-computed Path, delete guarded against children/products - counterparties: filter by kind (Supplier/Customer/Both), full join with Country - products: includes joined lookups, filter by group/isService/isWeighed/isActive, search by name/article/barcode, write replaces Prices+Barcodes atomically Role semantics: - SuperAdmin: mutates global references only - Admin: mutates/deletes tenant references - Manager: mutates tenant references (no delete on some) - Storekeeper: can manage counterparties and products (but not delete) All endpoints guarded by [Authorize]. Multi-tenant isolation via EF query filter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
fd2f5ae4f3 |
Phase 0: project scaffolding and end-to-end auth
- .NET 8 LTS solution with 7 projects (domain/application/infrastructure/api/shared/pos.core/pos[WPF]) - Central package management (Directory.Packages.props), .editorconfig, global.json pin to 8.0.417 - PostgreSQL 14 dev DB via existing brew service; food_market database created - ASP.NET Identity + OpenIddict 5 (password + refresh token flows) with ephemeral dev keys - EF Core 8 + Npgsql; multi-tenant query filter via reflection over ITenantEntity - Initial migration: 13 tables (Identity + OpenIddict + organizations) - AuthorizationController implements /connect/token; seeders create demo org + admin - Protected /api/me endpoint returns current user + org claims - React 19 + Vite 8 + Tailwind v4 SPA with TanStack Query, React Router 7 - Login flow with dev-admin placeholder, bearer interceptor + refresh token fallback - docs/architecture.md, CLAUDE.md, README.md Verified end-to-end: health check, password grant issues JWT with org_id, web app builds successfully (310 kB gzipped). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |