Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
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>
260 lines
13 KiB
Markdown
260 lines
13 KiB
Markdown
# Глоссарий 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](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 |
|