6 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bf53629092 |
refactor(units): drop Description, hide Code from non-SuperAdmin UI
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 1m24s
CI / Web (React + Vite) (push) Successful in 42s
Docker API / Build + push API (push) Successful in 1m33s
Docker Web / Build + push Web (push) Successful in 37s
Docker API / Deploy API on stage (push) Successful in 18s
Docker Web / Deploy Web on stage (push) Successful in 12s
Description у пяти канонических ОКЕИ-единиц никогда не заполнялось ни UI,
ни импортом, ни сидером — выкидываем поле полностью (Domain → EF-config
→ DTO → Input → frontend types → Super-Admin форма). Migration
Phase5d_DropUnitOfMeasureDescription дропает колонку.
Code оставляем в БД (нужен для интеграций МойСклад/1С), но скрываем от
org Admin'а:
- /catalog/units-of-measure — только колонки Name + кнопка toggle, без
Code и Description; поиск/сортировка только по Name.
- /super-admin/units-of-measure — Code продолжает показываться в таблице
и форме редактирования.
Дропдаун единиц в ProductEditPage / ProductQuickCreateModal уже отдаёт
только {u.name} в options, проверено. На SupplyEditPage/RetailSaleEditPage
в строках документа отображается unitName, Code не показывался — без
изменений.
|
||
|
|
493ed33fd0 |
phase5c: единицы измерения — глобальный справочник + junction для орг
Some checks failed
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 59s
CI / Web (React + Vite) (push) Successful in 42s
Docker API / Build + push API (push) Successful in 1m26s
Docker Web / Build + push Web (push) Successful in 35s
Docker API / Deploy API on stage (push) Failing after 38s
Docker Web / Deploy Web on stage (push) Successful in 12s
Было: каждая орга держала свои 5 копий («штука», «кг», ...). 95 строк в БД на 19 орг — duplication, любой Admin мог их редактировать. Стало: 5 globals (OrganizationId=NULL), CRUD только у SuperAdmin. Орга включает нужные единицы у себя через junction org_units_of_measure. Backend: - UnitOfMeasure: добавлен IsActive (для soft-delete с filtered unique index) - Новый OrgUnitOfMeasure (junction PK Organization+Unit, FK Restrict) - Migration Phase5c_UnitsOfMeasureGlobal: безопасная для prod — поднимает по одной строке на (Code, Name) до global, remap'ит products.UnitOfMeasureId, наполняет junction по факту существующих привязок, удаляет дубликаты. - /api/catalog/units-of-measure для org Admin: read-only список enabled-globals + POST/DELETE /enable для toggle - /api/super-admin/units-of-measure: full CRUD; DELETE soft (IsActive=false) с 409 если есть products или active org-junction (со списком орг) - DevDataSeeder.SeedTenantReferencesAsync вместо создания per-tenant юнитов — auto-enable всех active globals через junction Frontend: - /catalog/units — checkbox-список (включить/выключить); CTA на платформу для SuperAdmin - /super-admin/units — full CRUD над глобалами, 409 со списком организаций при попытке деактивировать используемую единицу |
||
|
|
5c5b231157 |
feat(directories): двухуровневые справочники Группы и Ед.измерения (системные + tenant)
Концепция: ProductGroup и UnitOfMeasure становятся двухуровневыми
справочниками. Системные эталонные записи (OrganizationId=NULL,
управляются SuperAdmin'ом) видны всем tenant'ам как «Эталон»
и read-only. Tenant'овские (OrganizationId=<orgId>) — обычная изоляция,
полный CRUD у админа орги.
Архитектура:
- IOptionalTenantEntity { Guid? OrganizationId } — новый интерфейс
в Domain/Common. ProductGroup и UnitOfMeasure отнаследованы от
Entity и реализуют его.
- AppDbContext.ApplyOptionalTenantFilter<T>: query-filter для
IOptionalTenantEntity пропускает запись с OrganizationId=NULL для
всех tenant'ов + tenant'овские по выбранной orgId. SuperAdmin без
override видит всё, в override — только NULL+своё.
- StampTenant: при Add для IOptionalTenantEntity — null оставляется
если SuperAdmin без override (системная), иначе подставляется
tenant.OrganizationId.
- Миграция Phase4d_OptionalTenantOnDirectories: ALTER COLUMN
OrganizationId DROP NOT NULL на product_groups и units_of_measure.
Existing данные FOOD MARKET (11 групп, 5 единиц) сохраняются как
tenant'овские — additive change, ничего не теряется.
- DTO: UnitOfMeasureDto и ProductGroupDto получили nullable
OrganizationId; фронт читает его для показа badge «Эталон».
- Защита мутаций: PUT/DELETE контроллеры теперь возвращают Forbid()
если запись OrganizationId=null и юзер не SuperAdmin (только
суперадмин может править/удалять системные).
Frontend:
- Badge «Эталон» (indigo) рядом с именем системной записи в обеих
страницах.
- Клик по строке системной записи → alert «Изменения недоступны…».
- SuperAdmin sidebar: новые пункты «Группы (эталон)» (FolderTree)
и «Ед. измерения (эталон)» (Ruler) под «Справочники». Страницы
реиспользуют существующие компоненты — для SuperAdmin без override
фильтр возвращает все записи, что в Phase 4+ можно ужесточить
отдельным эндпоинтом «только системные» (?orgId=null).
Decision (нонстоп-выбор по ТЗ): nullable OrganizationId через
IOptionalTenantEntity, не sentinel Guid.Empty — чище, безопаснее,
ясная семантика. Существующие группы FOOD MARKET НЕ мигрированы в
системные (как просил юзер) — пусть SuperAdmin сам создаст эталоны.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
b79c71591d |
feat(phase3b): drop IsActive, add ShelfLifeDays, restore PriceType IsSystem/IsRequired
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 и OtherSystem-импортёр: больше не пишут IsActive. UI-перекомпоновка карточки товара (Phase3b пп.6/9), Supply Posted-toggle, PercentInput, ShelfLifeDays-фильтр и редизайн прайс-секции — отдельными коммитами далее по плану. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
654a8ba87d |
feat: strict OtherSystem schema — реплика потерянного f7087e9
Main расходился с БД стейджа (Phase2c3_MsStrict в history, но код ещё ссылался на VatRate etc.) — деплой ломался. Реплицирую удаление сущностей вручную, чтобы код совпадал с таблицами. Убрано (нет в OtherSystem — не выдумываем): - 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. Добавлено как в OtherSystem: - Product.Vat (int) + Product.VatEnabled — OtherSystem держит НДС числом на товаре. - KZ default VAT 16% — applied в сидерах и в OtherSystemImportService когда товар не принёс свой vat. OtherSystemImportService: - 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 безопасен. |
||
|
|
cb66684134 |
phase1a: catalog domain (countries, currencies, vat, units, counterparties, stores, retail points, products)
Domain (foodmarket.Domain.Catalog): - Global references: Country (ISO-2), Currency (ISO-3 + symbol + minor unit) - Tenant references: VatRate (Percent + IncludedInPrice + IsDefault), UnitOfMeasure (ОКЕИ code + DecimalPlaces) - Counterparty: kind (Supplier/Customer/Both), type (Legal/Individual), BIN/IIN/TaxNumber, bank details - Store + RetailPoint with fiscal placeholders - ProductGroup: hierarchy via ParentId + denormalized Path - PriceType (Розничная/Оптовая), Product (article, VAT, group, supplier, flags IsService/IsWeighed/IsAlcohol/IsMarked, min/max stock) - ProductPrice (composite unique product+priceType), ProductBarcode (EAN13/EAN8/CODE128/UPC), ProductImage Infrastructure: - CatalogConfigurations with fluent API (indexes, precision 18/4 for money, FK with Restrict) - 13 new DbSets on AppDbContext + builder.ConfigureCatalog() - Migration Phase1Catalog — adds countries, currencies, vat_rates, units_of_measure, counterparties, stores, retail_points, product_groups, price_types, products, product_prices, product_barcodes, product_images Seeders: - SystemReferenceSeeder (always): 12 countries (KZ, RU, CN, TR, …), 5 currencies (KZT primary, RUB, USD, EUR, CNY) - DevDataSeeder extended: for Demo Market seeds VAT (0%, 12% default+included), units (шт/кг/л/м/уп), price types (Розничная default, Оптовая), main store, POS-1 Total DB schema: 26 tables. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |