1. Docs cross-check — обновил performance-baseline.md (Sprint 18/20/23 фиксы), secrets.md (16 новых env-vars из Sprint 20+ — Authentication Google/Microsoft, Monitoring, Cleanup, Hangfire:Cron, Telegram, Maintenance, App, Storage, PUBLIC_GA_ID/YM_ID). 2. Auto-gen api-reference — ApiReferenceDocsJob (Hangfire weekly вс 05:30 UTC) + Python-эквивалент `/tmp/gen-api-ref.py` для commit actual snapshot. docs/api-reference.md = 195 endpoints, 57 controllers. 3. Coverage gap-fill — Sprint18To23FeaturesTests.cs (16 Facts): - bulk-update + cross-tenant isolation - UserPresets CRUD - inline-edit price PATCH - CSV import 2 строки транзакцией - OrgExport create + list isolation - 1C-CSV import с русскими заголовками - audit-log export CSV streaming + BOM check - MoySklad sync-status stub - SSO providers + 503 unconfigured + 400 unknown provider - bug-001 NUL byte → 400 - bug-004 tiny price → 400 - export CSV BOM Покрывает все новые контроллеры Sprint 18-23 + regression-protect для критичных багов. 4. Contract tests — deploy/swagger-diff.sh: pull /swagger/v1/swagger.json с двух URL, diff endpoints+schemas через python3. Exit 0/1/2 для blue-green safety gate. Multi-path auto-detect. 5. docs/error-codes.md — каталог HTTP-кодов API (200-503) + humanizeError pattern для фронта + retry-policy таблица. 6. docs/glossary.md — 50+ доменных терминов (Tenant/Organization/Stock/ StockMovement/RetailSale/Counterparty/Owner/Employee/Role/Permission/ advisory lock/Serializable/…) с ссылками на code-сущности. 7. docs/ONBOARDING.md — first 3 days для нового разработчика (install → запуск → структура → первый PR + FAQ). 8. README.md — обновил под текущее состояние: React 19, Sprint-history 1-24, ссылки на ключевые docs, корректный 5-min quick start. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
13 KiB
Глоссарий food-market
Доменные термины, которые используются в коде, документации и общении
с пользователями. Один термин — одно определение. Ссылки на код через
file:line или namespace.path.
Базовые сущности
Organization (Организация, tenant)
Корневая сущность мульти-tenancy. Один процесс API обслуживает много
организаций; каждая видит только свои данные через query-filter по
OrganizationId. Не tenant-scoped сама по себе (отношение «один-ко-многим»
с TenantEntity).
Code: foodmarket.Domain.Organizations.Organization (src/food-market.domain/Organizations/Organization.cs).
См. MULTI-TENANCY.md.
TenantEntity / ITenantEntity
Базовый класс/интерфейс для всех domain-сущностей с OrganizationId.
AppDbContext автоматически применяет query-filter по reflection.
Code: foodmarket.Domain.Common.TenantEntity + ITenantEntity.
IOptionalTenantEntity
Двухуровневые справочники: либо системная запись (OrganizationId=null,
видна всем, мутирует только SuperAdmin), либо tenant'овская.
Пример: UnitOfMeasure, ProductGroup — есть глобальные «штука», есть
кастомные.
User
Учётная запись для логина (ASP.NET Identity). НЕ привязан к одной org —
один email может работать в нескольких организациях через Employee.
Code: foodmarket.Domain.Identity.User.
Employee (Сотрудник)
Запись о работнике конкретной org. Может иметь User (для логина) или
быть «без аккаунта» (только в чеках/документах). Связан с EmployeeRole.
Code: foodmarket.Domain.Organizations.Employee.
Owner / AccountOwnerUserId
Первый пользователь, создавший org через signup. Хранится в
Organization.AccountOwnerUserId. Не удаляется (кроме как через
SuperAdmin reassign).
Role / EmployeeRole / RolePermissions
- Identity Role (ASP.NET) — системная:
SuperAdmin,Admin,Cashier,Storekeeper,Manager. - EmployeeRole — per-org кастомная роль (например, «Старший кассир»),
привязана к сотруднику. Имеет
RolePermissions(флаги типаProductsEdit,RetailSalesOperate). - Permission — атрибут
[RequiresPermission("Name")]на endpoint'е. ПроверяетRolePermissionsсотрудника текущего юзера.
Store (Склад)
Физическое место хранения остатков. У org может быть несколько; первый
после signup — «MAIN» store.
Code: foodmarket.Domain.Organizations.Store.
RetailPoint (Касса / торговая точка)
Привязана к Store, к ней привязывается RetailSale. Может иметь фискальные
поля (FiscalSerial, FiscalRegNumber).
Code: foodmarket.Domain.Organizations.RetailPoint.
Каталог
Product (Товар)
Единица каталога. Имеет несколько Prices (по типам), Barcodes, Images,
принадлежит ProductGroup. Поля Sprint 19: IsArchived, IsAvailableForSale.
Code: foodmarket.Domain.Catalog.Product.
ProductGroup (Группа товаров)
Иерархическая (через ParentId + Path). Корень — «Все товары».
Может быть системной (OrganizationId=null) или per-org.
Code: foodmarket.Domain.Catalog.ProductGroup.
ProductPrice (Цена)
Один товар × один PriceType = одна цена. Тип может быть «системным»
(IsSystem — основная розничная) или «обязательным» (IsRequired — без неё
нельзя сохранить товар).
Code: foodmarket.Domain.Catalog.ProductPrice.
PriceType (Тип цены)
Розничная / Закупочная / Базовая / Себестоимость и т.д. Per-org. Sprint 1.
Code: foodmarket.Domain.Catalog.PriceType.
ProductBarcode (Штрихкод)
Уникальный (составной UNIQUE: Code + Organization). Один товар может
иметь несколько штрихкодов; один из них — IsPrimary (показывается на
этикетке).
Code: foodmarket.Domain.Catalog.ProductBarcode.
UnitOfMeasure (Единица измерения)
шт / кг / л / м / упак. Системные (OrganizationId=null) + org-кастомные.
OrgUnitOfMeasure — таблица per-org enable/disable.
Code: foodmarket.Domain.Catalog.UnitOfMeasure.
Counterparty (Контрагент)
Поставщик (Supplier) / Покупатель-юрлицо (LegalEntity) / Покупатель-физлицо
(Individual). Имеет БИН/ИИН, банковские реквизиты, контакты.
Code: foodmarket.Domain.Catalog.Counterparty.
Остатки и движения
Stock (Остаток)
Кеш SUM(StockMovement.Quantity) для пары (Store, Product).
Поддерживается транзакционно в каждом posting'е документа.
Code: foodmarket.Domain.Inventory.Stock.
StockMovement (Движение остатка)
Имматериальная запись об изменении остатка. Source: документ
(Supply.Post / RetailSale.Post / Enter.Post / Loss.Post / Transfer.Post /
Inventory.Post / SupplierReturn.Post / CustomerReturn.Post).
Инвариант: Stock.Quantity ≡ Σ StockMovement.Quantity для каждой
пары (Store, Product). Проверяется property-test'ом (Sprint 15).
Code: foodmarket.Domain.Inventory.StockMovement.
Документы (Documents)
Все имеют поля: Number, Date, Status (Draft/Posted), PostedAt.
Имеют IVersionedEntity для xmin concurrency check.
Supply (Приёмка)
От поставщика. Увеличивает остаток + пересчитывает скользящую
себестоимость (Product.Cost).
Code: foodmarket.Domain.Purchases.Supply.
Enter (Оприходование)
Внутреннее. Увеличивает остаток без поставщика. Для коррекций инвентаризации.
Code: foodmarket.Domain.Inventory.Enter.
Loss (Списание)
Уменьшает остаток. Причина: порча, кража, тестовое использование.
Code: foodmarket.Domain.Inventory.Loss.
Transfer (Перемещение)
Между складами. Уменьшает на исходном, увеличивает на целевом.
Code: foodmarket.Domain.Inventory.Transfer.
Inventory (Инвентаризация)
Списки фактических остатков. Расхождение → автоматические Enter/Loss
строки при post.
Code: foodmarket.Domain.Inventory.InventoryDoc (имя класса не Inventory из-за конфликта с namespace).
RetailSale (Розничный чек)
Продажа через POS / админку. После Post → уменьшает остаток, пишет ОФД
снапшот (FiscalNumber etc., Sprint 11), уведомляет SignalR.
Code: foodmarket.Domain.Sales.RetailSale.
Demand (Оптовая отгрузка)
Продажа юрлицу. Аналогично RetailSale, но с накладной (печатной формой).
Code: foodmarket.Domain.Sales.Demand.
SupplierReturn (Возврат поставщику)
Sprint 5. Уменьшает остаток + возвращает деньги поставщику.
Code: foodmarket.Domain.Purchases.SupplierReturn.
CustomerReturn / RetailSale.IsReturn=true
Возврат от покупателя. Реализован через флаг IsReturn на RetailSale +
ReferenceSaleId. Восстанавливает остаток.
Деньги
Cost (Себестоимость)
Скользящее среднее (qty_old × cost_old + qty_in × price_in) / (qty_old + qty_in).
Пересчитывается на каждой проведённой Supply. Decimal(18,4).
ReferencePrice (Эталонная цена закупа)
Опциональная. Заполняется автоматически unit-price'ом первой Supply; после 30 дней без новых Supply → Hangfire-job переписывает на Cost.
VAT (НДС)
Product.Vat(default изCountry.VatRate, в РК — 12%).Product.VatEnabled— управляет видимостью поля на UI.- На документах:
VatMode(включается «в том числе» / «сверху»).
AllowFractionalPrices (Дробные цены)
Org-настройка. Если false → все цены округляются до целых при сохранении. Sprint 23 bug-004: round-then-validate чтобы избежать «0 цена прошла required-check».
Доступ и безопасность
Tenant context
ITenantContext (resolved per request) выдаёт OrganizationId из JWT
claim org_id. NULL для unauthenticated / SuperAdmin-без-override.
SuperAdmin
Системная роль. Видит все organizations + может «открыть как…» через
X-Org-Override header (включает Admin claim для этой org'и).
Все действия SuperAdmin'a в override-режиме пишутся в super_admin_audit_log.
OrgAuditLog
Per-tenant журнал каждой mutate-операции (CREATE/UPDATE/DELETE на
любую TenantEntity). Пишется автоматически через OrgAuditInterceptor
на SaveChanges.
Permission
Атрибут [RequiresPermission("ProductsEdit")] на endpoint'е. Проверяет
флаг RolePermissions сотрудника текущего юзера. Если у юзера нет
Employee в этой org — 403.
Фоновые операции
Hangfire job
.NET background job framework. recurring-job (по cron) и
background-job (одноразовый). Хранятся в схеме hangfire той же БД.
advisory lock (Sprint 18)
PostgreSQL pg_advisory_xact_lock(int, int) — кооперативная блокировка
per-(org, doctype). Используется для сериализации генерации номера
документа.
Serializable transaction
PostgreSQL Isolation Level. Используется в posting'ах документов
(RetailSale.Post, Supply.Post, etc.) для защиты от race на
остатках. На конфликте → 40001, теперь мапится в 409 (Sprint 23).
Внешние интеграции
ОФД (OFD)
Оператор Фискальных Данных. В РК: Webkassa, Kassa24, ОФД-Соло.
RetailSale.Post после успеха отправляет фискальный документ → получает
FiscalNumber, FiscalQrCode. Sprint 11 scaffolding.
МойСклад
Сторонняя SaaS-система учёта. Импорт товаров/контрагентов/остатков по
OAuth-token (per-org в Organization.MoySkladToken).
POS (касса)
WPF-приложение под Windows 10+. Локальный SQLite-буфер, синк через
/api/pos/v1/* с idempotency-ключом (см. pos_batch_acks).
Telegram bot
Один platform-bot (token в env). Owner'ы org'и привязывают свой chat-id
(Organization.OwnerTelegramChatId) для получения daily-сводки.
Тестирование
Stage
https://test.admin.food-market.kz. Контейнеры на prod-vm
192.168.1.190, deploy через ~/deploy-stage.sh.
Smoke / Regression / Verify
- Smoke — быстрый sanity-check (5 шагов signup → login → bootstrap).
- Regression — полный e2e через Playwright (44 spec'a в Sprint 23).
- Verify — спринт-специфичные post-feature тесты.
Сокращения
| Сокр | Что |
|---|---|
| AT | Access Token (JWT, TTL 1h) |
| RT | Refresh Token (для получения нового AT) |
| PoS | Point of Sale (касса) |
| ОФД | Оператор Фискальных Данных |
| БИН | 12-цифровой номер юрлица в РК |
| ИИН | 12-цифровой номер физлица в РК |
| RPO | Recovery Point Objective (макс. потеря данных при backup-restore) |
| RTO | Recovery Time Objective (время восстановления) |
| CSP | Content Security Policy (HTTP-header) |
| SA | SuperAdmin |