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

Аудит ESWF / DOP — пост-MVP era

Дата: 2026-04-22 Автор: Claude Code Попередній: audit-2026-04-21.md — закритий 2026-04-22, всі базові фази виконано. Область: delta-аудит тільки нових артефактів (Phase D/E/F) + перегляд незавершеного backlog. Методологія: код-ревʼю нових 4 backend apps (hrm, production, baf_sync, budgeting) + сервісних розширень (crm/services, essentials/services/journal_entry, essentials/services/depreciation) проти усталених патернів проєкту.


0. TL;DR

Стан після Phase A → F-4: - 21 backend app (+4 нові MVP), 143 backend tests passing, 80 frontend tests. - Закриті: модулі hrm, production, baf_sync, budgeting + Manual JournalEntry + FixedAsset + 4 CRM analytics endpoints + 4 нові frontend reports (Trial Balance, P&L Drill-Down, ПСБО Form 1, Budget Variance). - Документація синхронізована (10 файлів оновлено / 2 створено).

Топ-5 знахідок цього мікро-аудиту:

  1. 🐛 Phase D — пропущений super() у CRM overrides. ActivityViewSet.perform_create і DealViewSet.perform_update не викликають super(), тому оминають TenantFilterMixin-логіку: tenant injection, created_by/updated_by, auto-numbering, audit log. Тести проходять, бо обходять API через ORM. Дрібний, але реальний баг — фікс ~5 рядків.
  2. ⚠️ Production MVP — розрив зі StockTransaction. complete_work_order пише напряму у PostingGroup, не оновлюючи StockTransaction/Batch/ItemWarehouseStock. Як наслідок — звіт «Inventory valuation» і «WorkOrder completion» можуть розходитись. Документовано в production-bom/README.md, але потребує rounded fix перед production.
  3. ⚠️ HRM MVP — спрощений UA-tax. Єдиний tax_rate_pct на gross — фактично placeholder. Реальний UA payroll: PDFL 18% + military 1.5% + ESV 22% з нарахування. Готовність до production вимагає або (a) розгортання повної UA tax-моделі або (b) явного маркеру «Demo-tax mode» у UI.
  4. ⚠️ BAF Sync — fake transport only. Реальний httpx.Client не реалізовано; всі production-екземпляри сидять у dry-run. Інтеграція не може бути ввімкнена для реального BAF без додаткових 30-50 рядків коду.
  5. 🧪 Frontend tests gap. 9 нових frontend-компонентів (5 dashboards + 4 forms) — 0 тестів. Існуючі 80 vitest-тестів покривають базову інфраструктуру (stores, API client, ErrorBoundary, LoginPage), але не нові business-screens.

Решта детально — у §§ 1-6.


1. Що змінилось з 2026-04-21 (delta)

1.1 Backend apps

App До Після
core, essentials, fleet, transport, registers, crm, eswf_chat, news, shop, mobile_api, sales_mobile_api, sales_field, medoc_exchange 17 17 (без змін у списку)
hrm +4 моделі (Position, Employee, PayrollPeriod, PayrollSlip)
production +3 моделі (BOM, BOMLine, WorkOrder)
baf_sync +3 моделі (Settings, Mapping, Log)
budgeting +2 моделі (Budget, BudgetLine)
Разом 17 21

1.2 Нові моделі в існуючих app-ах

  • essentials.JournalEntry + JournalEntryLine — Phase E.
  • essentials.FixedAsset + DepreciationEntry — Phase E.
  • essentials.EssentialsModuleSettings.default_*_operation × 4 — Phase A (вже були в коді, але не експоновані).

1.3 Нові endpoints

/api/v1/essentials/reports/trial-balance/         (Phase B — frontend)
/api/v1/essentials/reports/profit-loss/drill-down/
/api/v1/essentials/reports/psbo-form1/
/api/v1/essentials/journal-entries/                              (Phase E)
/api/v1/essentials/journal-entry-lines/
/api/v1/essentials/fixed-assets/                                 (Phase E)
/api/v1/essentials/fixed-assets/{id}/{depreciate,dispose}/
/api/v1/essentials/fixed-assets/run-monthly/
/api/v1/essentials/depreciation-entries/
/api/v1/crm/analytics/{funnel,stage-duration,win-loss,manager-activity}/   (Phase D)
/api/v1/hrm/{positions,employees,payroll-periods,payroll-slips}/  (Phase F-1)
/api/v1/production/{boms,bom-lines,work-orders}/                  (Phase F-2)
/api/v1/baf-sync/{settings,run,mappings,logs}/                    (Phase F-3)
/api/v1/budgeting/{budgets,budget-lines}/                         (Phase F-4)
/api/v1/budgeting/budgets/{id}/variance/

1.4 Нові frontend-компоненти

Категорія Файл
Reports TrialBalanceReport.tsx, PnlDrillDownReport.tsx, PsboForm1Report.tsx, BudgetVarianceReport.tsx
Forms JournalEntry/JournalEntryPage.tsx, FixedAsset/DepreciationRunner.tsx, BAFSync/BAFSyncPage.tsx
Dashboards CRM/SalesFunnelDashboard.tsx, CRM/ManagerActivityDashboard.tsx
Settings розширений AccountingSettings.tsx (4 default-BO selects)

1.5 Тести

Категорія Було Додано Стало
Backend (pytest) 86 +57 (11 CRM + 19 JE/FA + 9 HRM + 7 Production + 7 BAF Sync + 4 Budgeting) 143
Frontend (vitest) 80 0 80
Mobile 0 0 0

2. Знахідки за результатами імплементації

2.1 🐛 [BUG] Phase D — пропущений super() у CRM overrides

Де: backend/crm/views.py, ActivityViewSet.perform_create, DealViewSet.perform_update.

class ActivityViewSet(TenantFilterMixin, viewsets.ModelViewSet):
    def perform_create(self, serializer):
        from crm.services import record_activity
        instance = serializer.save()         # ❌ оминає TenantFilterMixin
        record_activity(instance)

Що саме оминається (master_data.py:87-126): - ❌ tenant injection (Activity створюється без tenant — впаде на CONSTRAINT тільки якщо tenant вимагається в моделі, інакше тихий tenant=NULL leak). - ❌ created_by/updated_by injection. - ❌ Auto-generated number для TransactionModel. - ❌ audit_service.log() — ця активність не з'являється в AuditLog.

Чому тести проходять: test fixtures створюють Activity напряму через ORM (Activity.objects.create(tenant=..., number=..., created_by=...)), оминаючи API. Потрібен інтеграційний test, що POST-ить через auth_client.post('/api/v1/crm/activities/', {...}).

Фікс (~5 рядків):

def perform_create(self, serializer):
    from crm.services import record_activity
    super().perform_create(serializer)   # ← робить tenant + created_by + number + audit
    record_activity(serializer.instance)

Те ж саме для DealViewSet.perform_update — викликати super().perform_update(serializer) ДО запису stage transition.

Пріоритет: 🟠 P1 (тихий баг — невидимий tenant leak).


2.2 ⚠️ Production MVP — розрив зі StockTransaction

Де: backend/production/services/completion.py.

complete_work_order створює PostingGroup + PostingEntry напряму, але: - ❌ Не створює StockTransaction (рух товарів на складах). - ❌ Не оновлює ItemWarehouseStock (залишки). - ❌ Не списує / не оприбутковує Batch (партіонний облік).

Наслідок: - Звіт BalanceSheet (через PostingGroup → 1100) покаже зміну вартості inventory. - Звіт InventoryValuation (через ItemWarehouseStock) покаже інше. - Розбіжність між finance- і inventory-картками.

Документовано: production-bom/README.md явно перелічує цю обмеженість як "MVP scope".

Фікс (правильний): 1. У complete_work_order після кожного PostingEntry зі quantity створювати StockTransaction (consumption: type='write_off' на input_warehouse; production: type='receipt' на output_warehouse). 2. Через batch_service оновлювати Batch.remaining_qty для FIFO write-off.

Пріоритет: 🟡 P2 (поки немає виробничого клієнта, MVP працює; перед production-deploy для виробництва — обов'язково).


2.3 ⚠️ HRM MVP — placeholder UA tax model

Де: backend/hrm/services/payroll.py:compute_slip.

tax_amount = (gross * tax_rate_pct / 100).quantize(Decimal('0.01'))

Один FK tax_payable_account + один tax_rate_pct — це різко спрощений сценарій. Реальна UA-зарплата:

Вирахування Ставка Кому платять Куди записати
ПДФО (Personal Income Tax) 18% від gross ДПС 4421 (Розрахунки за ПДФО)
Військовий збір 1.5% від gross ДПС 4422 (Військовий збір)
ЄСВ (роботодавець, нарахування) 22% від gross ПФУ 6450 + 4427
Утримання за пенс. внески — (зараз 0)

Фікс: PayrollSlip має 4-5 окремих полів tax_*_amount + 4-5 FK на CoA + service expand на 4 окремих posting-leg-и. Або винести у конфігурацію PayrollTaxSetting (per tenant, per year — щоб ставки можна було змінювати з історією).

Пріоритет: 🟡 P2 (демо-моделі достатньо для презентацій; production-клієнт UA — обов'язково).


2.4 ⚠️ BAF Sync — Fake transport, без реального httpx

Де: backend/baf_sync/services/sync.py.

Реалізовано: - BAFTransport Protocol — interface. - FakeTransport — для тестів і dry-run-проксі.

Не реалізовано: - HttpxTransport — реальний httpx.Client.post(url, json=payload, headers={'Authorization': f'Bearer {token}'}, timeout=30). - Retry / backoff. - Connection pooling. - Error mapping (5xx → retry, 4xx → fail, 401 → reauth).

Фікс (~30-50 рядків):

@dataclass
class HttpxTransport:
    def send(self, entity_type, payload, base_url, auth_token):
        url = f'{base_url}/{entity_type}'
        try:
            r = httpx.post(url, json=payload,
                           headers={'Authorization': f'Bearer {auth_token}'},
                           timeout=30)
            r.raise_for_status()
            return BAFResponse(success=True, baf_uuid=r.json().get('uuid', ''))
        except httpx.HTTPStatusError as e:
            return BAFResponse(success=False, detail=f'HTTP {e.response.status_code}: {e.response.text[:200]}')
        except httpx.RequestError as e:
            return BAFResponse(success=False, detail=f'Network: {e}')

Plus mechanism wiring: ViewSet BAFSyncRunView вибирає transport на основі settings.dry_run.

Пріоритет: 🟡 P2 (поки немає клієнта з BAF — MVP-демо достатньо).


2.5 🧪 Frontend tests gap

9 нових бізнес-компонентів — 0 тестів: - SalesFunnelDashboard.tsx, ManagerActivityDashboard.tsx - JournalEntryPage.tsx, DepreciationRunner.tsx, BAFSyncPage.tsx - TrialBalanceReport.tsx, PnlDrillDownReport.tsx, PsboForm1Report.tsx - BudgetVarianceReport.tsx

Існуючі 80 тестів покривають інфраструктуру (api/client, store/auth, ErrorBoundary), але не business-screens.

Опціональний фікс (vitest + RTL + msw): - Mock API через msw для кожного компонента. - Smoke-render + click "Generate"/"Save" → перевірка ключових елементів у DOM. - ~50-100 рядків на компонент × 9 = ~600 рядків test-коду.

Пріоритет: 🟢 P2-P3 (нижче за backend tests; feature complete = перевірка ручна, регресії ловлять бекенд-тести через API contracts).


2.6 Інші дрібні знахідки

# Що Де Пріоритет
6 Жоден з 4 нових app-ів не має seed-команди для демо-даних hrm/, production/, baf_sync/, budgeting/ 🟢 P3 — для DemoDataset перед презентаціями
7 JournalEntryPage.tsx робить N+1 PATCH (sync рядків через цикл, по одному API call на лінію) JournalEntryPage.tsx 🟢 P3 — перейти на bulk endpoint при перших 100+ рядкових JE
8 compute_variance(budget) робить N запитів до IncomeExpenseJournal (по одному на BudgetLine) budgeting/services.py 🟡 P2 — при сотнях рядків бюджету буде повільно. Фікс: один query з GROUP BY (account_id, EXTRACT(month FROM date)).
9 JournalEntryLineSerializer приймає entry з payload, але також прив'язується через ViewSet — ризик невідповідності accounting_extra.py 🟢 P3 — перевести на nested-serializer на JournalEntrySerializer
10 BAFSyncSettings.auth_token зберігається у plain text у БД baf_sync/models/settings.py 🟠 P1 — зашифрувати через cryptography (Fernet) перед prod-deploy

3. Що НЕ змінилось з 2026-04-21 (повторно — досі deferred)

Що Звідки Поточна позиція
CI/CD (GitHub Actions, production Dockerfile, pre-commit) audit-2026-04-21 §10 Фаза 2 ⏸️ Свідомо відкладено до колективної розробки. Trigger: перший зовнішній PR / prod-deploy.
God-components refactor audit-2026-04-21 §10/13 ⏸️ Активна розробка створює ризик конфліктів. Trigger: стабілізація 1+ місяць без значущих змін у ChatPage, AdminToolsPage, ChatDrawer, ETTNPage, DevToolsPage, SalesFieldManager.
Mobile tests audit-2026-04-21 §6 ⏸️ 0 tests для mobile/, mobile-sales/. Не пріоритет, бо мобільні застосунки relatively stable і не змінювались.
Logistic backend audit-2026-04-21 §1.1 🧩 Свідомо за межами community-build. Зараз — еталон exclusion-flow, не fault.
Consolidator audit-2026-04-21 §8 🔮 Backlog — немає клієнта з groupp consolidation.
Headless e-commerce engine audit-2026-04-21 §1.4 🔮 Backlog — Store Manager (DOP-license-store) покриває поточні потреби.
Client Portal B2B розширення audit-2026-04-21 §1.4 🔮 Backlog — поточний Next.js frontend/shop/ достатній для MVP.

4. План робіт на наступний цикл

Пріоритет від найшвидшого / найдешевшого до глибшого. P0 = цього тижня, P1 = цей місяць, P2 = квартал, P3 = backlog.

Фаза G — Cleanup після MVP-ери (1-2 дні, P0/P1)

Мета: закрити tech-debt-знахідки §2, не додаючи нових фіч.

  1. 🐛 Виправити Phase D super()-bug (P1, ~10 рядків): додати super().perform_create() / super().perform_update() у ActivityViewSet/DealViewSet. Додати інтеграційний тест, що перевіряє: POST /api/v1/crm/activities/ створює Activity з правильним tenant + audit_log entry.

  2. 🔐 Зашифрувати BAFSyncSettings.auth_token (P1): додати cryptography.fernet.Fernet, ENV-var BAF_TOKEN_SECRET. Migration з конвертацією існуючих plain-text-значень (якщо є).

  3. compute_variance — bulk-query (P2): замість N запитів — один з GROUP BY (account_id, EXTRACT(month)) + JOIN на BudgetLine. Покрити test'ом на 100+ рядкових бюджетах.

  4. 📦 Seed-команди для нових app-ів (P3): seed_hrm, seed_production, seed_budgeting (генерують 5-10 типових записів для демо). baf_sync — без seed (треба реальний BAF).

Фаза H — Зміцнення accounting основи (1-2 тижні, P1)

  1. 💼 Production MVP — bridge with StockTransaction (P2): complete_work_order після PostingGroup має створити StockTransaction (write_off на input_warehouse, receipt на output_warehouse) + апдейт ItemWarehouseStock. Інтеграційний test «Production cycle» що перевіряє consistency через inventory/valuation/ endpoint vs BalanceSheet-1100.

  2. 🇺🇦 HRM MVP — реалізація 4-leg UA tax (P2): розширити PayrollSlip 4 полями pdfl_amount, military_amount, esv_employee_amount, esv_employer_amount + 4 FK на CoA. compute_slip = ПДФО18% + Військ1.5% + ЄСВ22%/нарахування. Service post_slip створює 4 окремих posting-leg-и. Або (краще) винести у PayrollTaxConfig per tenant per year з історією ставок.

  3. 🌐 BAF Sync — реальний httpx.Client (P2): додати HttpxTransport (~30 рядків). Wire-in у BAFSyncRunView: transport = HttpxTransport() if not settings.dry_run else None. Mock-test через httpx.MockTransport.

Фаза I — End-to-end перевірки (3-5 днів, P2)

  1. 🧪 Frontend smoke-tests (P2-P3): для 9 нових business-компонентів — vitest + RTL + msw mock. Тільки smoke (render + один-два user-event), без deep-coverage.

  2. 🔁 Integration tests (P2): для кожного нового app — кінцевий test-сценарій (наприклад, для HRM: створити Position → Employee → Period → Slip → POST compute → POST post_document → перевірити PostingGroup в звіті Trial Balance).

Фаза J — Нові фічі (квартал+, P2-P3)

  1. JournalEntry templates / recurring — вирішення місячного резерву через одноразову проводку, що автоматично повторюється кожен місяць. Trigger: запит CFO про resource accruals.

  2. 📊 Budget approval workflowBudgetApproval модель + 3-step approval (draft → submitted → approved/rejected). Trigger: запит у multi-stakeholder-команді.

  3. 📈 Pipeline History dashboard в CRM — окремий drill-down звіт з фільтрами по менеджеру / клієнту / стадії. Trigger: запит CRM-команди на «conversion-кей-метрики».

  4. 🔄 BAF Sync — pull-flow (зараз тільки push): отримання змін з BAF → апдейт DOP-довідників + конфлікт-резолюція (last-write-wins / manual queue).

  5. 🏭 Production — multi-level BOM + WIP costing.

Фаза K — Backlog без планової дати (P3)

  • 🔮 Consolidator (intercompany elimination).
  • 🔮 Client Portal B2B-розширення (statement of account, individual pricing, returns).
  • 🔮 Headless e-commerce engine (backend/ecommerce/ з LiqPay, Нова Пошта).
  • 🔮 VRP (route optimization) для Logistic.
  • 🔮 ПСБО Форма 1 → офіційна форма (через accounting-ua).
  • 🔮 Tax-accounting у DOP (повний lifecycle ПН в DOP замість M.E.Doc).

5. Метрики (стан на 2026-04-22, друга сесія)

Метрика До 21-го 21-го 22-го Δ
Backend apps 15 17 (+2 plugins) 21 +4 нові MVPs
Backend tests 0 86 143 +57 (+66%)
Frontend tests (ERP) 80 80 80 0
Backend models — essentials 42 42 46+ +JE/JEL +FA +Depreciation
Реалізовані horizontal модулі 4 4 8 +HRM, +Production, +Budgeting, +BAF Sync (was integration)
Frontend reports 4 4 (+ PartyLedger, Matching) 8 (+ TrialBalance, PnL Drill, PSBO, BudgetVariance) +4
Phase 4 нових модулів planned 4 done MVP DONE
God-components (≥1000 рядків) 6 6 6 0 (не торкались)
Документи в docs/ 60 60 62 +2 (journal-entry.md, fixed-assets.md)
Documented status mismatch між doc/code багато mostly resolved 0 resolved

Test breakdown (2026-04-22)

core:                          21 tests   (TenantFilterMixin, UniversalViewSet, permissions, plugin-exclusion)
essentials:                    66 tests   (party_ledger 12, reports 21+10, serializers, JE 6, FixedAsset 13)
crm:                           11 tests   (pipeline_history, services, analytics endpoints)
hrm:                            9 tests   (compute_slip, post_slip, endpoint smoke)
production:                     7 tests   (BOM constraint, complete_work_order, cancel)
baf_sync:                       7 tests   (FakeTransport, dry_run, only_posted, settings)
budgeting:                      4 tests   (variance computation, period filter, endpoint)
medoc_exchange:                 6 tests   (existing — Phase 3 tax invoice lifecycle)
                              ─────
TOTAL:                        143 passing

6. Рекомендований найближчий крок

Старт: Фаза G — швидкі правки (1-2 дні).

Аргумент: §2.1 (super() bug) — тихий tenant-leak, який варто закрити раніше, ніж він проявиться у production. §2.5 (auth_token шифрування) — security hygiene перед першим живим клієнтом BAF Sync. Решта §2.6 — на свій розсуд.

Після Фази G можна або (a) переходити до Фази H (зміцнення Production/HRM), або (b) залишити Phase G як один маленький cleanup-цикл і йти до нових фіч (Фаза J).


7. Глибинний аудит блоків Essentials (додано 2026-04-22)

Контекст: раніше ми зробили подібний аудит блоку Покупки в Essentials (тоді він мав лише прихідну накладну). Після порівняння з SAP / Odoo / 1C / Dynamics з'явився повний flow PO → GR → PI з 3-way matching + Quality Control plugin (Receiving Dock → QC Cage → Stock).

Цей аудит застосовує ту саму методологію до решти блоків. Ціль: не просто перерахувати моделі, а критично оцінити чи блок виконує бізнес-функцію (proper posting flow + reports + workflow), або це лише CRUD-заглушки.

Класифікація: ✅ fully wired (post → registers → reports + workflow) · ⚠️ half-wired (часткове posting/reports) · 🟧 stubs only (CRUD endpoints без бізнес-логіки).

7.0 Що знайдено — реальний стан (без wishlist'у)

Блок Posting service Реєстри (CashJournal/InventoryJournal/PartyLedger) Reports / aggregations Workflow actions Класифікація
Sales (Invoice/Shipment/IncomingPayment) post_invoice_*, post_incoming_payment ✅ PartyLedger, CustomerOrderLedger, PlannedPaymentJournal ⚠️ P&L, BS, PaymentCalendar (без AR aging UI поза PartyLedgerReport) ✅ post/unpost + recalculate_status + Kanban ⚠️ half-wired
Inventory (Receipt/Shipment/Writeoff/Batch) receive_batch/deduct_fifo/reverse_* (WAC + FIFO) ✅ InventoryJournal + StockTransaction + ItemWarehouseStock ✅ InventoryValuation (FIFO vs WAC) ✅ post_document + BatchPickerModal ✅ fully wired (basic)
Bank/Cash (IncomingPayment/OutgoingPayment + Bank Exchange) post_incoming_payment/post_outgoing_payment (із 3-way-match-блокуванням) ✅ CashJournal ⚠️ CashFlow + PaymentCalendar (без bank reconciliation статусу) ⚠️ post/unpost + parse-only (не auto-create) ⚠️ half-wired
Payroll (HRM MVP) ⚠️ compute_slip + post_slip (placeholder UA tax) — (тільки PostingGroup) ❌ NONE ⚠️ compute + post_document + post_all 🟧 stubs+
Production (BOM/WorkOrder MVP) ⚠️ complete_work_order (без StockTransaction!) — (PostingGroup без InventoryJournal) ❌ NONE ⚠️ complete + cancel 🟧 stubs+
Fixed Assets (FixedAsset MVP) post_depreciation + run_monthly_depreciation — (PostingGroup + DepreciationEntry) ⚠️ З BalanceSheet (через 1100 рах.), без власного asset register ⚠️ depreciate + run-monthly + dispose (без gain/loss) 🟧 stubs+

Критична знахідка: з 6 блоків — 1 fully wired (Inventory), 2 half-wired (Sales, Bank/Cash), 3 stubs+ (Payroll, Production, Fixed Assets). Користувацька гіпотеза «склад може бути заглушкою» спростована: inventory-сервіси (469 рядків batch_service.py + 402 рядки goods_receipt_service.py) реалізують справжній FIFO/WAC. Натомість Production MVP дійсно «не виконує своєї функції зі складом» — посту в PostingGroup є, але StockTransaction / Batch / ItemWarehouseStock НЕ оновлюються (підтверджено §2.2).


7.1 SALES — Order-to-Cash

Що реально працює: - Invoice → planned payment → actual payment → party-ledger sync (через post_invoice_party_ledger + post_incoming_payment_party_ledger). - GoodsShipment з FIFO write-off + COGS posting. - recalculate_status синхронізує paid_amount/shipped_amount з пов'язаних документів. - InvoiceKanbanBoard — візуалізація 8-status workflow (draft / sent / partially_paid / paid / shipped / closed / overdue / cancelled). - AR aging фактично доступне через PartyLedgerReport (з drill-down).

Gap-таблиця проти SAP/Odoo/1C/Dynamics:

# Фіча SAP SD Odoo Sales 1С УТ Dynamics 365 SCM DOP зараз Пріоритет Trigger
S-1 Sales Order як окремий документ перед Invoice (резервування + контроль наявності) ✅ Sales Order ✅ Sale Order ✅ Замовлення покупця ✅ Sales Order ❌ Quotation→Invoice напряму 🟠 P1 Замовлення на склад > 1 дня
S-2 Customer credit limit + блокування при перевищенні ✅ Credit Limits ❌ Поле є (Contract.credit_limit?), не enforced 🟠 P1 Перший збиток від невплати
S-3 Returns / Credit Note (повернення від клієнта з reverse FIFO) ✅ Refund ✅ Повернення ✅ Sales Return ❌ NONE (workaround: GoodsWriteoff + Invoice negative) 🟠 P1 Перше повернення
S-4 Pricelist scheduling (валідні from-to + multi-tier) ✅ Condition records ✅ Pricelist + rules ✅ Тип цін з історією ✅ Sales Pricing Hierarchy ⚠️ ItemPrice без valid_from/valid_to 🟡 P2 Регулярні промо
S-5 Discounts (% + abs + cascade + per-line/document) ⚠️ InvoiceLine.discount_pct тільки на лінії 🟡 P2 Запит торгової акції
S-6 Sales analytics dashboard (по менеджеру / клієнту / товару / періоду) ✅ COPA ✅ Sales analysis ✅ Аналіз продажів ✅ Power BI sales ❌ Тільки P&L (агрегований) 🟡 P2 Запит CFO/COO
S-7 AR aging report (current / 30 / 60 / 90 / 120+) ✅ Partner Ledger ✅ Звіт по дебіторах ✅ Aging by buckets ⚠️ PartyLedger показує суми, але без bucket-розкладки 🟠 P1 Перший fin.controller
S-8 Statement of Account (акт звірки) ❌ NONE 🟠 P1 Перший аудит
S-9 Quotation→Invoice convert + Quotation expiry ✅ Quotation ✅ + auto-expire ✅ КП ✅ Sales Quote ⚠️ Quotation в crm/, але convert-to-order не створює Invoice 🟡 P2 CRM team workflow
S-10 Recurring/Subscription invoices (auto-create N invoices з шаблону) ✅ Subscriptions ✅ Підписки ✅ Recurring billing ❌ NONE 🟢 P3 SaaS-клієнт
S-11 Multi-currency invoice (invoice валюта ≠ tenant base) з FX rate locking ⚠️ Invoice.currency є, але без auto FX-locking 🟡 P2 Перший import-клієнт

7.2 INVENTORY — Stock & Batch Management

Update 2026-05-09: P0/P1 gaps з цієї секції закрито у planning/inventory-overhaul-2026-05-09.md. Деталі статусів — нижче в gap-таблиці.

Що реально працює: - FIFO write-off через batch_service.deduct_fifo (тепер з expiry policy + reservation respect — Phase 9, 2026-05-09). - WAC perpetual moving average через receive_batch (оновлює ItemWarehouseStock.average_cost). - StockTransaction як append-only audit log (filterable за item/warehouse) — додано тип TRANSFER (Phase 8). - 6 стандартизованих звітів через Report Framework: Stock Balance / Inventory Turnover / Item Card / Batch Aging / Batch Movements / Expiring Batches (Phase 5+9). - BatchPickerModal дзеркалить FIFO на frontend для preview. - GoodsWriteoff тепер створює PostingGroup (Phase 0 fix — раніше тихо drift'ив). - InventoryCount документ + from-stock ergonomy action (Phase 4+6). - StockTransfer документ (Phase 8). - CurrencyRevaluation документ + інтегровано у Period Close wizard (Phase 7). - L4 / L5 reconciliation перевіряють cross-domain integrity (Phase 2). - Batch reservations + expiry policy block_expired_shipment (Phase 9).

Critical gap (закрито 2026-05-09): - ✅ Inventory Count / Cycle Count документ — реалізовано Phase 4 (InventoryCount + InventoryCountLine, BO Surplus/Shortage).

Gap-таблиця:

# Фіча SAP MM Odoo Inventory 1С УТ Dynamics SCM DOP зараз Пріоритет Trigger
I-1 Inventory Count / Cycle Count документ ✅ Physical Inventory ✅ Inventory Adjustment ✅ Інвентаризація ✅ Counting Journal DONE 2026-05-09 (Phase 4) 🔴 P0 Перший аудит / квартальна інвентаризація
I-2 Stock Transfer between warehouses (single document) ✅ Transfer Order ✅ Internal Transfers ✅ Переміщення ✅ Transfer Journal DONE 2026-05-09 (Phase 8) 🟠 P1 Перший multi-warehouse клієнт
I-3 Reservation (резерв партії під замовлення) ✅ Reserved/Available ✅ Резервування ✅ Reservation hierarchy DONE 2026-05-09 (Phase 9 — BatchReservation + available_qty; SalesOrder integration — окрема таска) 🟠 P1 E-commerce-flow
I-4 Reorder point + reorder quantity + auto-PO suggestion ✅ MRP ✅ Reordering Rules ✅ Параметри номенклатури ✅ Item coverage ❌ NONE 🟡 P2 Перший production-ready stock manager
I-5 Lot/Batch expiry tracking + alerts ✅ Batch management ✅ Lots & Expiry ✅ Серії ✅ Lot expiration DONE 2026-05-09 (Phase 9 — block_expired_shipment policy + expiring-batches report) 🟠 P1 Харчова/фарма
I-6 ABC analysis (за обертом / маржею) ✅ ABC Classification ✅ ABC-аналіз ✅ Item Velocity ❌ NONE 🟢 P3 Запит retail/wholesale
I-7 Inventory turnover report + aging slow-movers DONE 2026-05-09 (Phase 5 — inventory-turnover + batch-aging reports) 🟡 P2 Запит фінансиста
I-8 Serial number tracking (унікальні серійники, не партії) ✅ Serialized stock ✅ Lots & Serial ✅ Серії ✅ Tracking dimensions ❌ NONE (Batch ≠ serial) 🟢 P3 Електроніка/обладнання клієнт
I-9 Кит-set / bundle assembly (без production WO) ✅ Kits ✅ Комплектація ✅ Kits / Templates ❌ NONE 🟢 P3 Retail multi-pack
I-10 Multi-UoM (закуповуємо у коробках, продаємо штуками) ✅ Alternative UoM ✅ Multiple UoM ✅ Багато од. виміру ✅ Multiple UoM ⚠️ Item.unit єдина 🟡 P2 Дистрибутор
I-11 Stock-out alerts (push notification або email) ❌ NONE 🟢 P3 E-commerce backbone

7.3 BANK / CASH

Що реально працює: - Posting incoming/outgoing payment у CashJournal. - 3-way-matching блокування OutgoingPayment, якщо PurchaseInvoice не пройшла match. - BankExchangeProcess: parse CSV → preview (БЕЗ автоматичного створення payment-документів — лише UI-helper). - CashFlow report (Direct Method) + PaymentCalendar (planned vs actual).

Critical gaps: - 🟥 Bank reconciliation документ відсутній. Tobто немає способу: «у виписці є проводка, у DOP — немає; або в DOP є, у виписці — немає». Це типовий end-of-month процес для будь-якого фін.відділу. - 🟥 Internal cash transfer (між касами / з каси на рахунок) — відсутній. Зараз тільки IncomingPayment / OutgoingPayment, що передбачає клієнта/постачальника.

Gap-таблиця:

# Фіча SAP FI Odoo Accounting 1С Бух Dynamics F&O DOP зараз Пріоритет Trigger
B-1 Bank Reconciliation документ (matched/unmatched) ✅ Bank Reconciliation ✅ Виписка банку (повний flow) ✅ Bank Reconciliation ❌ Тільки parse, не reconcile 🔴 P0 Перший живий клієнт
B-2 Internal Transfer документ (Cashbox→Bank, Bank→Bank) ✅ Internal Transfers ✅ Переміщення коштів ✅ Bank Funds Transfer ❌ NONE (workaround: Out + In вручну) 🟠 P1 2+ касові точки
B-3 Cash Count / Касовий ордер (РКО+ПКО з підписом) ✅ Друкована форма ПКО/ВКО ⚠️ IncomingPayment/OutgoingPayment без друкованої форми ПКО/ВКО 🟡 P2 UA-аудит
B-4 Bank statement auto-import (формати IBAN/CSV/MT940) ✅ Multi-format ✅ + auto-pull from bank ✅ Клієнт-Банк ✅ Electronic banking ⚠️ CSV є, MT940/MTBank/IBAN — NONE 🟡 P2 Mass adoption
B-5 FX revaluation на кінець періоду (валютний рах. → переоцінка курсу) ✅ FX Revaluation ✅ Currency Revaluation ✅ Переоцінка валюти ✅ Foreign Currency Revaluation ❌ NONE 🟠 P1 Перший валютний клієнт
B-6 Payment matching (auto-match payment to invoice by amount/ref) ✅ Auto-clearing ✅ Bank Reconciliation matching ✅ Списання боргу ✅ Settlement ⚠️ IncomingPayment.invoice FK є, але без auto-suggest 🟡 P2 Bulk payments
B-7 Cash forecast (на наступні N днів, з рахунками planned) ✅ Cash Position ✅ Cash Flow Forecast ✅ Платіжний календар ✅ Cash Flow Forecasting ⚠️ PaymentCalendar є, але без forecast > 60 днів 🟡 P2 CFO daily
B-8 Cheque/Promissory note tracking ✅ Чеки ✅ Bills of Exchange ❌ NONE 🟢 P3 Експорт-клієнт
B-9 Multi-currency cashbox (одна каса з кількома валютами) ⚠️ Cashbox.currency єдина 🟡 P2 Клієнт з валютною касою

7.4 PAYROLL (HRM)

Що реально працює: - compute_slip: gross = base × days/standard, tax = gross × rate, net. - post_slip: PostingGroup з 4 entries (gross + tax withholding). - post_period: bulk-post всіх drafts.

Critical gaps: - 🟥 Tax model один прапорець — повторно з §2.3. - 🟥 Timesheet немаєdays_worked зашитий у slip. - 🟥 Vacation/Sick balance не tracked.

Gap-таблиця:

# Фіча SAP HCM Odoo HR/Payroll 1С ЗУП Dynamics HR DOP зараз Пріоритет Trigger
P-1 Timesheet (день+години+проект) ✅ CATS ✅ Timesheets ✅ Табель ✅ Timesheet ❌ days_worked напряму у slip 🟠 P1 Project-based billing
P-2 Vacation/Sick leave balance + request workflow ✅ Time mgmt ✅ Time Off ✅ Відпустки ✅ Leave & Absence ❌ NONE 🟠 P1 5+ employees
P-3 UA tax 4-leg (PDFL18% + Військ1.5% + ЄСВ22% з нарахування) — UA-локалізація — UA-локалізація ❌ Один tax_rate_pct 🔴 P0 UA production
P-4 Payroll register (звіт за період по всіх slip) ✅ Payroll Posting ✅ Payslip register ✅ Реєстр ЗП ❌ NONE 🟠 P1 Перший live payroll
P-5 KPI / bonus calculation engine ✅ Compensation ✅ Преміювання ✅ Performance ❌ NONE 🟡 P2 Sales-team з bonus
P-6 Pay grades / salary bands ✅ Compensation ✅ Сітки ✅ Compensation Plans ⚠️ Position.base_salary без grade-діапазону 🟢 P3 HR-driven org
P-7 Payslip PDF (з підписом, штрих-кодом для працівника) ❌ NONE 🟠 P1 Перша видача ЗП
P-8 Garnishments / utримання за рішенням суду ⚠️ кастом ❌ NONE 🟢 P3 Алименти-кейс
P-9 Year-end tax report (1ДФ для UA) — UA-локалізація — UA-локалізація ✅ 1-ДФ ❌ NONE 🟡 P2 Перша річна звітність
P-10 Onboarding / Termination workflow + автогенерація документів ✅ Recruitment + Contract ✅ Кадрові накази ✅ Employee lifecycle ❌ NONE (поля hire_date/termination_date є) 🟢 P3 HR-driven org

7.5 PRODUCTION

Що реально працює: - complete_work_order: PostingGroup з consumption + output legs (placeholder cost=1).

Critical gaps: - 🟥 Production не оновлює Inventory — повторно §2.2. Це основна функція виробничого обліку. - 🟥 Реальна собівартість не calculated — unit_cost=1 placeholder. Тобто блок «не виконує своєї функції» по cost accounting.

Gap-таблиця:

# Фіча SAP PP Odoo MRP 1С ERP Dynamics SCM DOP зараз Пріоритет Trigger
Pr-1 StockTransaction integration (consumption з warehouse, production на warehouse) ❌ Posting є, StockTransaction — NONE 🔴 P0 Перший production-cycle
Pr-2 Real costing (FIFO/WAC з batch_service.deduct_fifo) ✅ Standard/Actual ✅ FIFO/Standard ✅ Calculated на основі залишків ✅ Standard/Actual/FIFO ❌ unit_cost=1 placeholder 🔴 P0 Перший financial close після production
Pr-3 Multi-level BOM (дерево: subassembly → assembly) ✅ Phantom BOM ✅ Багаторівнева ❌ NONE 🟠 P1 Складальне виробництво
Pr-4 Work Center + Operations + Routing (технологія) ✅ Routing ✅ Routing & Workorders ✅ Маршрути ✅ Route ❌ NONE 🟡 P2 Виробництво з машинним часом
Pr-5 WIP (Work-in-Progress) accumulation account ✅ Cost Account by stage ✅ НВ ✅ WIP Account ❌ Нет — все одразу йде на 1100 🟠 P1 Multi-day production
Pr-6 Yield/Scrap tracking (фактичний випуск vs план + причини) ✅ + Quality Alerts ✅ Браку ✅ Production journal ❌ NONE 🟡 P2 Перший client з браком
Pr-7 Capacity planning (calendar заняття Work Centers) ✅ Capacity ✅ Manufacturing Calendar ✅ Завантаження ✅ Production Schedule ❌ NONE 🟢 P3 High-utilization shop
Pr-8 Subcontracting (передача матеріалів підряднику) ✅ Subcontracting ✅ Subcontracting ✅ Переробка ✅ Subcontracting ❌ NONE 🟢 P3 Outsourced manufacturing
Pr-9 Quality control on production output (відбракування браку) ✅ QM ✅ Quality ✅ Якість ✅ Quality ❌ NONE (можна reuse essentials_quality plugin?) 🟡 P2 Регульована галузь
Pr-10 Production planning (MRP — auto-generate WO з sales forecast) ✅ MRP ✅ MPS+MRP ✅ Планування ✅ Master Planning ❌ NONE 🟢 P3 Make-to-Stock виробник

7.6 FIXED ASSETS

Що реально працює: - post_depreciation + run_monthly_depreciation (idempotent, 3 методи). - dispose (status-маркер, БЕЗ gain/loss проводки).

Critical gaps: - 🟥 Capitalization workflow відсутній. Зараз FixedAsset створюється як master-data через CRUD без зв'язку з PurchaseInvoice. Має бути: «купили обладнання → PurchaseInvoice з прапорцем is_capex → автоматично створюється FixedAsset зі ссилкою на PI». - 🟥 Disposal без gain/loss — повторно §2.6 (поле 4 з таблиці tech-debts).

Gap-таблиця:

# Фіча SAP FI-AA Odoo Accounting 1С Бух Dynamics F&O DOP зараз Пріоритет Trigger
F-1 Acquisition від PurchaseInvoice (capex flag → auto-create FixedAsset) ✅ Asset acquisition ✅ Linked from vendor bill ✅ ОЗ через ПрН ✅ Acquisition Proposal ❌ Тільки manual creation 🟠 P1 Перший capex spend
F-2 Disposal з gain/loss проводкою ✅ Asset retirement ✅ Asset Disposal ✅ Списання ОЗ ✅ Asset Disposal ⚠️ status-маркер без проводки 🟠 P1 Перше вибуття
F-3 Asset Transfer between departments / locations ✅ Переміщення ОЗ ❌ NONE 🟡 P2 Multi-location клієнт
F-4 Asset register report (інвентарна книга) ✅ Asset Explorer ✅ Asset Register ✅ Інвентарна книга ✅ Asset register ❌ NONE (тільки через generic MasterDataPage) 🟠 P1 Аудит / інвентаризація
F-5 Depreciation schedule preview (forecast: ось так буде амортизуватись) ✅ Depreciation Run Simulation ✅ Forecast ✅ Прогноз ✅ Asset Forecast ❌ NONE (monthly_depreciation() для 1 місяця) 🟡 P2 CFO planning
F-6 Component-level depreciation (двигун автомобіля окремо від кузова) ✅ Sub-assets ⚠️ Кастом ✅ Складові ОЗ ✅ Asset components ❌ NONE 🟢 P3 Industrial assets
F-7 Revaluation / Impairment (переоцінка + знецінення) ✅ Revaluation ⚠️ Manual JE ✅ Переоцінка ✅ Revaluation ❌ NONE (можна вручну через JournalEntry) 🟢 P3 ПСБО / IFRS аудит
F-8 Tax depreciation (паралельно бух — інша ставка/метод) ✅ Tax depreciation ⚠️ Кастом ✅ Податковий облік ✅ Tax depreciation books ❌ NONE 🟢 P3 UA-локалізація tax
F-9 Repair / maintenance log + cost accumulation ✅ Plant Maintenance ⚠️ Maintenance ✅ Ремонти ✅ Asset Maintenance ❌ NONE (можна reuse Fleet MaintenanceRecord?) 🟢 P3 Heavy equipment client

8. Зведена таблиця пропозицій (для узгодження перед імплементацією)

Як читати: P0 = блокер для production, P1 = очікується першим клієнтом, P2 = квартал, P3 = backlog. Trigger = подія, яка зробить фічу обов'язковою.

8.1 Top-12 пропозицій з §7 (відсортовано за P+blast-radius)

Update 2026-05-09: Усі 4 Inventory-related рядки з топ-5 (#1 InventoryCount, #5 Stock Transfer, плюс Phase 9 reservation + expiry, плюс Phase 7 currency revaluation) — закрито в одну сесію planning/inventory-overhaul-2026-05-09.md.

# Блок Фіча Прі Оцінка Чому ця, чому зараз
1 Inventory Inventory Count / Cycle Count документ 🔴 P0 3-5 днів ✅ DONE 2026-05-09 (Phase 4).
2 Bank/Cash Bank Reconciliation документ 🔴 P0 5-7 днів Місячний обов'язковий процес для бухгалтера; зараз DOP лише parse-CSV без matching.
3 Production StockTransaction + real costing у complete_work_order 🔴 P0 3-5 днів Production MVP не виконує своєї функції — повторно §2.2.
4 Payroll UA tax 4-leg (PDFL+Військ+ЄСВ роботодавець+утримання) 🔴 P0 5-7 днів Без цього модуль не для UA-production; повторно §2.3.
5 Inventory Stock Transfer документ 🟠 P1 2-3 дні ✅ DONE 2026-05-09 (Phase 8).
6 Sales AR aging report (current/30/60/90/120+ buckets) 🟠 P1 2-3 дні PartyLedger показує тотал, але fin.controller хоче buckets.
7 Sales Statement of Account (акт звірки) 🟠 P1 2-3 дні Стандартний місячний документ для клієнтів.
8 Sales Sales Order як окремий документ перед Invoice (з резервуванням) 🟠 P1 5-7 днів Розривається з резервуванням inventory (#I-3).
9 Sales Returns / Credit Note (з reverse FIFO) 🟠 P1 3-5 днів Перше повернення зламає поточний flow.
10 Bank/Cash Internal Transfer (Cashbox→Bank, Bank→Bank) 🟠 P1 2-3 дні 2+ касові точки → необхідно.
11 Fixed Assets Acquisition від PurchaseInvoice (capex flag → auto-create FA) 🟠 P1 3-5 днів Зараз FA створюється manually у відриві від PI — розрив у обліку.
12 Fixed Assets Disposal з gain/loss проводкою 🟠 P1 2-3 дні Тільки status-маркер не закриває потребу повноцінного вибуття.

8.2 Cluster-варіанти для імплементації

Варіант A — «Fix what's broken first» (P0-only, 2 тижні) Закриває критичні P0 з §8.1 #1-4. Після цього всі реалізовані MVP можна назвати "production-ready" для UA-клієнта. - Inventory Count + Bank Reconciliation + Production-StockTransaction integration + UA payroll tax. - Тести + документація для кожного.

Варіант B — «Sales completion» (P1, 1.5 тижні) Закриває §8.1 #6-9: AR aging + Statement of Account + Sales Order + Returns. Sales-блок стає fully-wired (з half-wired).

Варіант C — «Multi-warehouse readiness» (P1, 1 тиждень) Stock Transfer + Internal Cash Transfer + Inventory Count = повноцінне багатоскладське/багатокасове підприємство.

Варіант D — «Fixed Assets completion» (P1, 1 тиждень) Acquisition flow + Disposal з gain/loss + Asset register report.

Варіант E — «Все зразу» (4-5 тижнів) P0 + P1 = 12 фіч. Серйозне закриття всіх відомих gap'ів. Production-ready ERP.


9. Запит на узгодження

Як обговорено: перед імплементацією потрібне узгодження.

Запитання до користувача: 1. Який варіант з §8.2 обираємо: A (P0-only fix), B (Sales), C (Multi-warehouse), D (FA), E (all P0+P1), або custom-вибір окремих рядків з §8.1? 2. Чи додати наступний рівень — повну «глобальну» візію Essentials з порівнянням SAP MM-FI-CO повного циклу + дорожньою картою на півроку? Чи поточного scope §7-§8 достатньо? 3. Чи є специфічні клієнти / сценарії, які треба підняти у пріоритеті (наприклад, якщо найближчий клієнт — фарма з expiry tracking, треба додати I-5 у P0)?


Пов'язане

  • audit-2026-04-21.md — попередній (closed) аудит з детальним TODOs.
  • DOCS.md — документаційний hub (оновлено 22-го).
  • todo.md — загальний backlog.
  • BACKLOG.md — автогенерований backlog із ## 🔮 Deferred / Ideas.
  • CLAUDE.md — оновлений 22-го (21 apps, нові endpoints).

Наступна точка для перегляду — після Фази G (cleanup) або відразу після Фази H (зміцнення) залежно від темпу.