Перейти до змісту

ESWF · Data Model Overview

Для кого: новий розробник або AI-агент, що вперше відкриває репо. Мета — за 15 хвилин отримати картину, як влаштовані дані ESWF/DOP, без читання backend/*/models/ (130+ ORM-класів у 23 аппах).

Що ВНУТРІ: базові абстракції, multi-tenancy на рівні моделей, категорії моделей, карта крос-аппових FK, posting bridge (документ → бух. проводка), реєстри, multi-ledger, app-індекс.

Що ЗОВНІ: API contract, RBAC/security, frontend-↔-backend конвенції, deployment — це окремі гепи, які закриватимуться іншими доками (див. docs/audit-2026-04-22.md).


1. Базові класи

Уся бізнес-модель росте з 5 абстрактних класів у backend/core/models/base.py:

                    TenantAwareModel  (uuid, tenant, created/updated_at/by)
            ┌───────────────┼─────────────────┬──────────────────┐
            │               │                 │                  │
    MasterDataModel   TransactionModel   RegisterModel    (прямі нащадки —
            │         (number, date,     (date, document_       PostingEntry,
            │          state, status,     type, document_id,    Batch, ...)
            │          description)       manual_edit)
   HierarchicalMasterDataModel
       (parent, is_group)
Клас Призначення Ключові поля Приклади моделей
TenantAwareModel Корінь усіх таблиць даних. Прив'язка до тенанта + аудит. uuid, tenant, created_at/by, updated_at/by напряму використовується нечасто; всі інші наслідують
MasterDataModel Довідники (каталоги). Логічне видалення через deletion_mark. code, name, name_ua, is_active, deletion_mark, description Item, Client, Organization, Warehouse, Driver, Employee
HierarchicalMasterDataModel Деревовидні довідники. + parent (self-FK), is_group ChartOfAccounts, ExpenseItem, Vehicle, Container
TransactionModel Бізнес-документи. Дві незалежні осі: state (життєвий цикл) і status (бізнес-воркфлоу). number, date, state ∈ {draft, posted, marked}, status, description Invoice, GoodsReceipt, Waybill, PayrollSlip, WorkOrder, Deal
RegisterModel Регістри/журнали накопичення. Завжди мають backref на документ. date (indexed), document_type, document_id, manual_edit PostingGroup, CashJournal, InventoryJournal, IncomeExpenseJournal

Чому 4 категорії, а не загальний BaseModel? Кожен клас несе свою семантику доступу і поведінку: - Master → CRUD з deletion_mark, не видаляється фізично. - Transaction → workflow (draft → posted), посилається з регістрів. - Register → write-once журнал, переписується тільки через анпост-репост документа (інваріант, див. §7).


2. Multi-tenancy на рівні даних

Принцип: одна БД, всі таблиці спільні, ізоляція через tenant FK. Schema-per-tenant НЕ використовується.

Request → TenantMiddleware → request.tenant
        ViewSet (TenantFilterMixin)
   queryset.filter(tenant=request.tenant)
        Serializer / Response
Шар Файл Що робить
Модель backend/core/models/base.py TenantAwareModel.tenant FK на core.Tenant (cascade) — успадковується всіма
Middleware backend/core/middleware/tenant.py Витягує тенанта з JWT-токена, кладе у request.tenant
ViewSet backend/core/views.py (TenantFilterMixin) Авто-фільтр queryset за request.tenant
Cascade DB-рівень on_delete=CASCADE на tenant FK → видалення тенанта чистить усі його дані

Винятки (моделі БЕЗ tenant)

App Моделі Чому
news Article, Category, Tag Глобальний блог, спільний для всіх тенантів
eswf_chat ChatSession, Message, ChatRoom, ... Standalone (legacy, не мігровано на TenantAware)
shop (більшість) Product, License, ActivationCode, ... SaaS-магазин — продукти спільні, тенанти купують ліцензії

⚠️ Gotcha з backend/CLAUDE.md: TenantFilterMixin підміняє серіалізатор для list — у новому ViewSet одразу перевизначайте get_serializer_class, інакше зламаються детальні відповіді.


3. Категорії моделей (зведення)

Загалом по проекту: ~130 ORM-класів + ~50 inline-subtables ≈ 180 таблиць.

Категорія К-сть Базовий клас Приклади
Master Data ~65 MasterDataModel Item, Client, Warehouse, Driver, Employee, FuelType, Position
Hierarchical Master ~6 HierarchicalMasterDataModel ChartOfAccounts, ExpenseItem, Vehicle, Container
Transactions ~35 TransactionModel Invoice, GoodsReceipt, Waybill, PayrollSlip, WorkOrder, Deal, Quotation
Registers / Ledgers ~22 RegisterModel PostingGroup, CashJournal, InventoryJournal, IncomeExpenseJournal, ContainerLedger
Reference / Enum-as-Table ~30 MasterDataModel (легкі) Unit, Currency, TaxRate, BusinessOperation, WorkSchedule
Config / Settings ~15 звичайні models.Model (singleton-стиль) EssentialsModuleSettings, FleetModuleSettings, GpsAddonSettings
Inline Subtables ~50+ звичайні models.Model з FK на parent InvoiceLine, GoodsReceiptLine, WaybillTask, BOMLine, PayrollSlipLine

Subtable-патерн: документ оголошує атрибут _subtables = [...], це використовується EntityRegistry для метадрівен-форм у фронтенді. Деталі — eswf/infrastructure/backend.md.


4. Карта модулів (high-level)

essentials — ядро. Усі вертикалі (fleet, production, hrm, crm) посилаються в нього за shared masters (Organization, ChartOfAccounts, Currency, Warehouse, Client, Department).

                        ┌──────────────┐
                        │ medoc_exchange│ (УКТЗЕД sync)
                        └──────┬────────┘
   ┌──────────────┐    ┌───────┴────────┐    ┌──────────────┐
   │     crm      │───►│                │◄───│      hrm     │
   └──────────────┘    │                │    └──────────────┘
   ┌──────────────┐    │                │    ┌──────────────┐
   │ production   │───►│   essentials   │◄───│    fleet     │
   └──────────────┘    │                │    └──────────────┘
   ┌──────────────┐    │  (ядро ERP:    │    ┌──────────────┐
   │  budgeting   │───►│   docs, CoA,   │◄───│ containerhub │
   └──────────────┘    │   posting,     │    │  (plugin)    │
   ┌──────────────┐    │   registers)   │    └──────────────┘
   │consolidation │───►│                │    ┌──────────────┐
   └──────────────┘    │                │◄───│ ess_quality  │
                       │                │    │  (plugin)    │
                       └──┬─────────┬───┘    └──────────────┘
                          │         │
                          ▼         ▼
                   ┌──────────┐  ┌──────────┐
                   │registers │  │  shop    │
                   │ (12 jrnl)│  │(SaaS)    │
                   └──────────┘  └──────────┘

   ┌──────────────┐                          ┌──────────────┐
   │sales_mobile_ │── tenant docs ───────►   │  baf_sync    │── external API
   │     api      │   (через essentials)      │ (singleton)  │
   └──────────────┘                          └──────────────┘

Легенда: стрілка A → B = моделі A мають FK на моделі B. Глобальні (без tenant) — news, eswf_chat, частина shop — на діаграмі не показані.


5. Cross-app FK реєстр

Шаблон: vertical-моделі тримають тонкі бізнес-поля, а спільні довідники беруть з essentials.

З моделі На модель (інший app) Кратність Призначення
hrm.Position essentials.Department N:1 Посада належить підрозділу
hrm.Employee hrm.Position, essentials.Department N:1, N:1 Працівник прив'язаний до посади/підрозділу
hrm.PayrollSlip essentials.Organization, essentials.ChartOfAccounts (×3) N:1 Рахунки для проводок ЗП (expense / payable / tax)
production.WorkOrder essentials.Organization, essentials.Warehouse, essentials.ChartOfAccounts (×4) N:1 Рахунки inventory / cogs / output / variance
production.BOM essentials.Item (output), essentials.Unit N:1 Готовий продукт + од. виміру
fleet.Waybill essentials.Organization, essentials.Department N:1 Транспорт прив'язаний до орг./підрозділу
fleet.TransportInvoice essentials.Organization, essentials.Client, essentials.ChartOfAccounts N:1 Рахунок-фактура за транспортні послуги
fleet.DriverSalaryAccrual essentials.Organization N:1 Нарахування зарплати водія
crm.Deal, crm.Quotation essentials.Currency, essentials.Client N:1 Сума угоди у валюті, клієнт
consolidation.ConsolidationGroupMember essentials.Organization N:1 Учасник холдингу
budgeting.BudgetLine essentials.ChartOfAccounts, essentials.Currency, essentials.BusinessDirection N:1 План по рахунку/проекту
containerhub.GateTransaction, TrainArrival essentials.Organization, essentials.Warehouse N:1 Логістична операція в орг./складі
essentials_quality.QualityInspection essentials.Item, essentials.Warehouse, essentials_quality.DefectReason N:1 Контроль якості при прийманні
sales_mobile_api.SalesReturn essentials.Client, essentials.Item, essentials.Warehouse N:1 Повернення товару з мобільного
medoc_exchange.UKTZEDDirectory (немає крос-FK) Глобальний довідник УКТЗЕД, читається essentials.Item як lookup

Анти-патерн, якого тут НЕМАЄ: circular import між apps. Усі стрілки односпрямовані → essentials ні на кого не посилається.


6. Posting bridge (документ ↔ облік)

Найважливіший шов архітектури. Усі бухгалтерські проводки створюються через одну пару моделей, незалежно від типу документа.

┌─────────────────────────┐
│   TransactionModel      │   (Invoice, Waybill, PayrollSlip,
│   state = "posted"      │    WorkOrder, GoodsReceipt, ...)
│   business_operation FK ├──┐
└───────────┬─────────────┘  │ шаблон проводки
            │                │ (Sales / Purchase / Salary / ...)
            │ post_xxx()     ▼
            │       ┌─────────────────────────┐
            │       │   BusinessOperation     │
            │       │   (template: rules для  │
            │       │    Дт/Кт + analytics)   │
            │       └────────────┬────────────┘
            ▼                    │
┌─────────────────────────┐      │
│   PostingGroup          │◄─────┘
│   (RegisterModel)       │
│   • document_type/id    │  ← backref на документ
│   • organization, ledger│
│   • amount, currency    │
│   • source_signature    │  ← SHA-256, ловить ручні правки
└───────────┬─────────────┘
            │ 1 : N
┌─────────────────────────────────────────────────┐
│   PostingEntry                                   │
│   • account (ChartOfAccounts)                    │
│   • debit / credit / quantity                    │
│   ─── Financial Dimensions (D365-style): ─────── │
│   • partner • product • warehouse • contract     │
│   • department • project • expense_item • person │
└─────────────────────────────────────────────────┘

Файли:

Що Файл
Моделі PostingGroup + PostingEntry backend/essentials/models/posting.py
Per-document пост-сервіси (post_invoice, post_payroll_slip, …) backend/essentials/services/posting.py
Універсальний build з BusinessOperation шаблону backend/essentials/services/accounting_service.py
Інваріант посту/анпосту (READ THIS) .claude/rules/document-posting.md

Інваріант (з .claude/rules/document-posting.md)

Якщо document.state == "posted", то для пари (document_type, document_id) повинна існувати щонайменше одна PostingGroup (і відповідні PostingEntry), сума яких збігається з документом.

Будь-яке розходження = баг. Анпост документа = видалення PostingGroup (cascade → entries). Репост = новий PostingGroup, не правка існуючого.

source_signature (SHA-256 від ключових полів документа на момент посту) дозволяє швидко детектувати ручне редагування проведеного документа без анпосту — кейс, який інакше веде до тихого розходження.

Financial Dimensions замість субконто

На відміну від класичного 1С/SAP-підходу зі стрімкими "субконто №1, №2, №3", ESWF тримає усі аналітики як окремі FK-колонки в PostingEntry. Документ при пості заповнює ті, що релевантні (наприклад, для Sales: partner + product + warehouse; для Salary: person + department + expense_item).

Деталі підходу — dop/modules/horizontal/accounting-tax/architecture.md.


7. Реєстри (Registers)

PostingGroup — це бухгалтерський регістр (Дт/Кт). Поряд із ним існують операційні регістри — для швидких звітів і контролю залишків без агрегації проводок.

Регістр App Що зберігає Хто пише
CashJournal / CashLedger essentials / registers Рух грошей по касах і рахунках IncomingPayment, OutgoingPayment, CashTransfer
InventoryJournal / StockTransaction essentials Рух товарів по складах + партії GoodsReceipt, GoodsShipment, GoodsWriteoff, Manufacturing
IncomeExpenseJournal essentials Доходи/витрати з аналітикою (для P&L) усі документи з виручкою/собівартістю
PartyLedger registers Розрахунки з контрагентами PurchaseInvoice, SalesInvoice, payments
PlannedPaymentJournal essentials План платежів (Cash Flow forecast) PlannedPayment
Ledger essentials Загальна Главна книга (агрегати по рахунку × періоду) derived з PostingGroup
ItemWarehouseStock essentials Поточні залишки (snapshot, не журнал) trigger з StockTransaction
FuelJournal / FuelLedger registers Рух пального Waybill, VehicleRefueling, FuelDrain
DriverSalaryLedger registers Нарахування ЗП водіїв DriverSalaryAccrual
ActivityLedger registers Активності CRM (подзвоники, зустрічі) crm.Activity
CustomerOrderLedger registers Залишки по замовленнях crm.SalesOrder
BlockedStockLedger essentials_quality Заблокований по якості stock QualityInspection
ContainerLedger / StorageChargeLedger containerhub Контейнери на терміналі + збори за зберігання ContainerMovement, DemurrageCalculation

Правило: регістри ніколи не редагуються вручну (manual_edit=False за замовчуванням). Зміна — тільки через анпост-репост документа-джерела. Виняток — поле manual_edit=True для legacy-міграцій.


8. Multi-Ledger та Consolidation

Multi-Ledger (multiple Charts of Accounts)

PostingGroup.ledger (FK на essentials.Ledger) дозволяє вести кілька паралельних книг на одну операцію:

                   Document (Sales Invoice)
                  post_invoice()
       ┌──────────────────┼──────────────────┐
       ▼                  ▼                  ▼
 PostingGroup       PostingGroup       PostingGroup
 ledger=NSBO        ledger=IFRS        ledger=Tax
 (укр. стандарт)   (міжнародний)      (податковий)

Один документ → N груп проводок → N паралельних звітів (Trial Balance, P&L, Balance Sheet) з різних точок зору. Деталі реалізації — planning/multi-coa-implementation.md.

Consolidation (холдинг)

App consolidation агрегує IncomeExpenseJournal через групи компаній з еліминацією внутрішньогрупових операцій:

Що Файл
Моделі ConsolidationGroup, ConsolidationGroupMember, IntercompanyMap backend/consolidation/models.py
Сервіс compute_consolidated_pnl() backend/consolidation/services.py
Endpoint GET /api/v1/consolidation/groups/{id}/pnl/?from&to

MVP-обмеження (Phase F-5): 100% володіння, одна валюта, тільки транзакційна еліминація.


9. App-індекс (23 backend-апи)

App К-сть моделей Категорія Роль
core infra infrastructure User, Tenant, AuditLog, базові класи, EntityRegistry, UniversalViewSet, TenantMiddleware
essentials ~46 horizontal · ядро Master data + транзакції + проводки + регістри + звіти. Серце системи.
registers 12 horizontal · регістри CashJournal, InventoryJournal, FuelJournal, PartyLedger, CustomerOrderLedger, ActivityLedger, ...
crm 9 horizontal Lead → Deal → Quotation → SalesOrder + analytics (funnel, win-loss)
hrm 4 horizontal · F-1 Position, Employee, PayrollPeriod, PayrollSlip
production 3 horizontal · F-2 BOM + BOMLine + WorkOrder; idempotent complete_work_order()
baf_sync 3 horizontal · F-3 Singleton settings + pluggable BAFTransport Protocol
budgeting 2 horizontal · F-4 Budget + BudgetLine; compute_variance() vs IncomeExpenseJournal
consolidation 3 horizontal · F-5 ConsolidationGroup + Member + IntercompanyMap; compute_consolidated_pnl()
fleet 33 vertical Vehicle/Driver/Route + Waybill + Refueling + GPS + eTTN (Ed25519)
transport 3 vertical · ref RailwayRoad, RailwayStation, Wagon
eswf_chat 5 feature AI chat (OpenRouter) + messenger (rooms)
news 3 feature Article, Category, Tag (глобальні)
shop 8 feature · SaaS Product, ActivationCode, ShopOrder, License, EmailSmtpSettings
medoc_exchange 2 integration UKTZEDDirectory, TaxInvoice (M.E.Doc)
sales_mobile_api 9 mobile SalesVisit, CallLog, DayPlan, SalesReturn — для мобільного Sales Rep
mobile_api 0 mobile · API only DRF endpoints для WatermelonDB sync (driver app)
sales_field 0 API only KPI, day plans, GPS compliance — supervisor endpoints
media 0 infra File storage config
eswf 0 infra Project settings, root urls.py
essentials_quality 5 plugin DefectReason, StorageLocation, QualityInspection, ReceiptFlow, BlockedStockLedger
containerhub 15+ plugin · vertical Container, Terminal, GateTransaction, TrainArrival, DemurrageCalculation, EDI
logistic plugin (frontend-only) Backend-app відсутній — еталонний exclusion-case для plugin system

Нумерація фаз (F-1..F-5) відповідає Phase F roadmap (2026-04-22). Шаблон додавання нового модуля — skill .claude/skills/new-module/.


10. Подальше читання

Тема Документ
Backend tech stack, URL-карта, gotchas eswf/infrastructure/backend.md
Архітектурний аудит (ризики, не "що є") eswf/architecture/audit.md
Concept deep-dive: 15 принципів обліку audit-concepts-2026-04-22.md
Financial Dimensions (D365-style) dop/modules/horizontal/accounting-tax/architecture.md
План рахунків (European PCG) dop/modules/horizontal/accounting-tax/accounting-plan.md
Document Operations (BusinessOperation шаблони) dop/modules/horizontal/essentials/document-operations.md
Multi-CoA / Multi-Ledger дизайн planning/multi-coa-implementation.md
Inventory ↔ Finance резонанс dop/modules/horizontal/essentials/inventory-finance.md
Інваріанти посту/анпосту (для редагування коду) .claude/rules/document-posting.md
Шаблон нового модуля .claude/skills/new-module/SKILL.md