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

Аналіз документа «Прихідна накладна» (Goods Receipt)

Дата аналізу: 2026-04-13 Мета: порівняти поточну реалізацію з SAP, D365, 1С, Odoo; визначити прогалини та план покращень.


1. Поточний стан реалізації

1.1 Бекенд

Моделі: - GoodsReceipt (backend/essentials/models/goods_receipt.py) — заголовок документа - Поля: supplier, organization, warehouse, contract, currency, business_operation, payment_type, total_amount - Автоперерахунок total_amount при зміні рядків - Стани: draft → posted → marked

  • GoodsReceiptLine — рядок документа
  • Поля: item, unit, quantity, price, amount, tax_rate/vat_rate/vat_amount/price_gross, batch_number, serial_number, expiry_date
  • Автоматичний розрахунок amount, vat_amount, price_gross при save()

Проведення (backend/essentials/views/transactions.py, GoodsReceiptViewSet.post_document): - Для кожного рядка: 1. Автогенерація batch_number якщо порожній ({doc.number}-{line.pk}) 2. Конвертація ціни в облікову та презентаційну валюту (convert_to_dual()) 3. Створення Batch + StockTransaction через receive_batch() 4. Створення запису InventoryJournal 5. Якщо є business_operation — бухгалтерські проводки (основна + ПДВ) - Відміна: reverse_batch_receipt() з guard (не можна якщо партії вже видані)

Сервіси: - batch_service.py — FIFO deduction, WAC calculation, receive/reverse batch, ItemWarehouseStock refresh - currency_service.py — dual-currency conversion (документна → облікова/презентаційна) - SELECT FOR UPDATE на Batch rows для race-condition safety

API endpoints:

GET/POST   /essentials/goods-receipts/
GET/PUT    /essentials/goods-receipts/{id}/
GET        /essentials/goods-receipts/{id}/form-data/    — документ + refs
GET        /essentials/goods-receipts/form-refs/          — refs + next_number
POST       /essentials/goods-receipts/{id}/post_document/
POST       /essentials/goods-receipts/{id}/unpost_document/
CRUD       /essentials/goods-receipt-lines/

1.2 Фронтенд

Компоненти (frontend/erp/src/components/Essentials/GoodsReceipt/):

Файл Призначення
GoodsReceiptPage.tsx Роутер: list / form / print
GoodsReceiptList.tsx Таблиця документів з фільтрами
GoodsReceiptForm.tsx Форма створення/редагування
GoodsReceiptFields.tsx Основні поля (2 fieldset: Контрагенти + Надходження)
GoodsReceiptSubtables.tsx Таблиця рядків з inline editing + modal
GoodsReceiptFormToolbar.tsx Панель дій
GoodsReceiptListToolbar.tsx Панель фільтрів списку

Поля заголовка: - Fieldset «Контрагенти»: Organization, Supplier, Contract - Fieldset «Надходження»: Warehouse, Currency, Business Operation, Payment Type

Рядки (SubtablePanel): - Inline edit: item, unit, quantity, price, vat_rate, batch_number - Modal edit: + sequence, tax_rate, serial_number, expiry_date - Автоматичне підтягування ціни/одиниці при виборі товару (/essentials/pricing/lookup/) - Footer: Amount | VAT | Total (з акцентним Total на синьому тлі)

Особливості: - Draft lines — локальні рядки до першого збереження (~115 рядків коду) - Custom post flow: create doc → flush draft lines → post - Автозаповнення валюти та payment_type з контракту - Фільтрація контрактів по supplier + organization


2. Порівняння з провідними DOP

2.1 SAP S/4HANA (MIGO)

Функція SAP ESWF Висновок
Зв'язок з Purchase Order GR прив'язується до PO Немає PO Відсутнє
3-way matching (PO ↔ GR ↔ Invoice) Автоматичний Відсутній Відсутнє
Tolerance check (допуски кількість/ціна) Configurable ±% Немає Відсутнє
Storage Location (зона/бін) Склад → Зона → Бін Тільки warehouse Часткове
Quality Inspection QM auto-trigger Відсутній Відсутнє
Return to Vendor Movement Type 122 Немає окремого документу Відсутнє
Batch Split (1 рядок → кілька партій) Так 1 рядок = 1 партія Часткове
Movement Types (200+) Так Тільки receipt Мінімальне
Freight / Landed Cost Так Відсутній Відсутнє
Dual-currency doc + local + group doc + accounting + presentation На рівні

2.2 Microsoft Dynamics 365 F&SCM

Функція D365 ESWF Висновок
Product Receipt (packing slip) Окремий етап Немає Відсутнє
PO workflow (PO → Receipt → Invoice) Так Тільки GR Відсутнє
Charges (фрахт, митні збори) На заголовку/рядках Відсутній Відсутнє
Inventory Dimensions Site → WH → Location → Batch → Serial WH + Batch + Serial Часткове
Financial dimensions on lines Cost center, department per line Тільки business_operation на заголовку Часткове
Over/under delivery Configurable per item Немає Відсутнє
Discount handling (рядкова/документна) Так Відсутній Відсутнє

2.3 1С:Підприємство

Функція ESWF Висновок
Документ-підстава (Замовлення → Надходження) Так Немає замовлення постачальнику Відсутнє
Кілька складів в одному документі Так (warehouse per line) Один склад на заголовку Обмеження
Характеристики номенклатури Розмір, колір, конфігурація Відсутні Відсутнє
Додаткові витрати з розподілом Пропорційно сумі/кількості Відсутній Відсутнє
Друковані форми (М-4, ТОРГ-12) Так PrintEngine (Fortune Sheet) На рівні
Штрихкод-сканер Пошук по штрихкоду Відсутній Відсутнє
Курсові різниці Автоматичний розрахунок Тільки конвертація на дату Часткове

2.4 Odoo 17

Функція Odoo ESWF Висновок
Multi-step receipt (Input → Quality → Stock) 1/2/3-step Тільки 1-step Часткове
Backorders Автоматичне створення Відсутнє Відсутнє
Landed Costs Окремий документ Відсутнє Відсутнє
Lot/Serial tracking rules per product Configurable Все однаково Часткове
UoM conversion (коробки → штуки) Автоматичний Одна одиниця Відсутнє

3. Виявлені прогалини

3.1 Критичні (відсутній базовий функціонал)

3.1.1 Знижки (Discounts)

  • Немає рядкових знижок (discount_percent)
  • Немає документних знижок
  • Формула: amount = qty × price × (1 - discount% / 100)

3.1.2 Додаткові витрати (Charges / Landed Cost)

  • Фрахт, страхування, митні збори — не враховуються у собівартості
  • Потрібна окрема таблиця витрат з розподілом по рядках (пропорційно сумі або кількості)

3.1.3 Purchase Order → Goods Receipt linkage

  • Не можна створити GR «на основі» замовлення постачальнику
  • Немає контролю «що замовлено vs. що отримано»
  • Немає backorder tracking

3.1.4 Конвертація одиниць виміру

  • Одна одиниця per line без конвертації
  • Постачальник продає в коробках, облік — у штуках → зараз неможливо

3.1.5 Warehouse per line

  • Один склад на весь документ
  • Одна накладна не може розподілити товар по кількох складах

3.2 Важливі (UX та якість)

3.2.1 Валідація при проведенні — мінімальна

  • Перевіряється лише: lines.exists() та line.item_id
  • НЕ перевіряється: warehouse, currency, organization ≠ null; quantity > 0; price >= 0

3.2.2 Ліміт 200 записів у refs

  • _build_refs()qs[:200] для всіх довідників
  • Якщо 500+ товарів — частина не потрапить у select
  • Потрібен server-side search (typeahead)

3.2.3 Немає optimistic locking

  • Два користувачі можуть одночасно редагувати — останній перезаписує зміни першого (lost update)

3.2.4 Post logic у ViewSet (anti-pattern)

  • post_document — 170+ рядків бізнес-логіки в HTTP handler
  • Неможливо перевикористати з management commands / Celery / tests

3.2.5 Draft lines дублювання

  • ~115 рядків складної логіки в GoodsReceiptForm.tsx для draft lines + custom post
  • Дублюється в кожній кастомній формі документа

4. Рекомендації по UI/UX

4.1 Sticky header документа

Зараз: Номер і дата ховаються при скролі рядків. Рекомендація: Зафіксований header з: - Номер + дата (великий шрифт) - Статус як кольоровий badge (draft=сірий, posted=зелений) - Постачальник (quick-view) - Загальна сума (завжди видима)

4.2 Розширений summary panel

Зараз: Amount | VAT | Total — тільки 3 числа. Додати: - Кількість позицій / загальна кількість одиниць - Розбивка по ставках ПДВ (20%: ₴X, 7%: ₴Y, 0%: ₴Z) - Вага брутто/нетто (якщо є на товарі)

4.3 Paste з Excel

  • Ctrl+V → розпарсити clipboard → створити рядки
  • Drag-and-drop зона для Excel/CSV файлів
  • Критично для масового введення

4.4 Покращений пошук товару

Зараз: Select з пошуком по назві. Додати: - Пошук по штрихкоду / артикулу / коду - Recent items (останні вибрані) вгорі списку - Quick-add «+ Створити» прямо з select'у - Показувати залишок на складі при виборі (inline підказка)

4.5 Візуальний status flow

[Draft] ─── Save ───→ [Saved] ─── Post ───→ [Posted]
                                           [Linked to Invoice]

4.6 Attachment'и

  • Скан накладної, сертифікат якості, фото вантажу
  • Прив'язка до заголовка або окремого рядка

4.7 Audit trail UI

  • Хто створив / змінив / провів / відмінив
  • Часові мітки всіх операцій
  • Порівняння версій (diff)

4.8 Multi-row operations в таблиці рядків

  • Multi-select (checkbox або Shift+Click) для bulk delete
  • Drag-and-drop reorder рядків
  • Auto-fill (drag cell value down)

5. Рекомендації по архітектурі

5.1 Винести post/unpost у сервіс

backend/essentials/services/goods_receipt_service.py

def post_goods_receipt(receipt: GoodsReceipt) -> PostResult:
    """Повна логіка проведення — batch, journal, accounting."""

def unpost_goods_receipt(receipt: GoodsReceipt) -> None:
    """Відміна проведення з guard-перевірками."""
ViewSet тільки делегує:
@action(detail=True, methods=['post'], url_path='post_document')
def post_document(self, request, pk=None):
    obj = self.get_object()
    result = post_goods_receipt(obj)
    if result.errors:
        return Response({'detail': ..., 'errors': result.errors}, status=400)
    return Response(self.get_serializer(obj).data)

5.2 Pre-posting validation

def validate_before_post(receipt: GoodsReceipt) -> list[str]:
    errors = []
    if not receipt.warehouse_id:
        errors.append("Warehouse is required")
    if not receipt.currency_id:
        errors.append("Currency is required")
    if not receipt.organization_id:
        errors.append("Organization is required")
    for line in receipt.lines.all():
        if line.quantity <= 0:
            errors.append(f"Line {line.pk}: quantity must be positive")
        if line.price < 0:
            errors.append(f"Line {line.pk}: price cannot be negative")
    return errors

5.3 Server-side search для refs

Замість завантаження всіх 200 записів — typeahead endpoint:

GET /essentials/items/?search=bolt&limit=20
На фронтенді: useRefSelect з debounced пошуком.

5.4 Optimistic locking

Додати version або використати updated_at для перевірки:

if obj.updated_at != payload.get('_updated_at'):
    raise ConflictError("Document was modified by another user")

5.5 Draft lines у useDocumentForm

Перенести draft-lines логіку з GoodsReceiptForm у useDocumentForm hook — прибере ~100 рядків з кожної кастомної форми.


6. Зведена оцінка

Аспект Оцінка Коментар
Базова функціональність 7/10 CRUD + проведення працює
Batch/Lot tracking 8/10 FIFO + WAC, dual-currency — на рівні
Accounting integration 7/10 PostingGroup + InventoryJournal
UI/UX форми 6/10 Функціональна, але базова
UI/UX списку 6/10 Стандартна таблиця, page_size=50
Валідація 5/10 Мінімальна перед проведенням
Повнота vs SAP/D365 4/10 Немає PO, charges, discounts
Повнота vs 1С 5/10 Немає замовлення, додаткових витрат
Архітектура 6/10 Post logic у ViewSet, ліміт refs
Data integrity 8/10 SELECT FOR UPDATE, guard on unpost

7. План імплементації покращень

Фаза 1 — Quick wins (1-2 дні кожне)

# Задача Тип Файли
1.1 Pre-posting validation (warehouse, currency, org, qty>0, price>=0) Backend views/transactions.py або новий services/goods_receipt_service.py
1.2 Знижка рядка (discount_percent на GoodsReceiptLine) Backend + Frontend models/goods_receipt.py, serializers/transactions.py, GoodsReceiptSubtables.tsx
1.3 Server-side search для items (замість ліміту 200) Backend + Frontend views/transactions.py, GoodsReceiptSubtables.tsx → typeahead
1.4 Розширений summary panel (к-сть позицій, розбивка ПДВ) Frontend GoodsReceiptForm.tsx footer
1.5 Sticky document header (номер, дата, статус, сума) Frontend DocumentFormShell.tsx або GoodsReceiptForm.tsx

Фаза 2 — Середня складність (3-5 днів кожне)

# Задача Тип Файли
2.1 Витяг post/unpost у goods_receipt_service.py Backend refactoring Новий services/goods_receipt_service.py, спрощення views/transactions.py
2.2 Warehouse per line (fallback на header) Backend + Frontend models/goods_receipt.py, GoodsReceiptSubtables.tsx
2.3 Paste з Excel (clipboard → lines) Frontend GoodsReceiptSubtables.tsx або SubtablePanel.tsx
2.4 Attached files (скан накладної, сертифікати) Backend + Frontend Нова модель DocumentAttachment, UI компонент
2.5 Audit trail UI Backend + Frontend Використати AuditLog, новий UI компонент
2.6 Optimistic locking Backend + Frontend TransactionModel, serializer validation
2.7 Draft lines → useDocumentForm Frontend refactoring useDocumentForm.ts, спрощення всіх кастомних форм

Фаза 3 — Стратегічні (1-2 тижні кожне)

# Задача Тип Файли
3.1 Purchase Order документ Backend + Frontend Нові models, serializers, views, компоненти
3.2 GR «на основі PO» (linkage + заповнення) Backend + Frontend goods_receipt.py → FK до PO, auto-fill logic
3.3 Landed Cost (додаткові витрати з розподілом) Backend + Frontend Нова модель GoodsReceiptCharge, розподіл по рядках
3.4 UoM conversion (коробки → штуки) Backend + Frontend Unit модель + conversion table, line-level calc
3.5 Multi-step receipt (вхідний контроль → якість → склад) Backend + Frontend Workflow engine, нові стани документа
3.6 3-way matching (PO ↔ GR ↔ Vendor Invoice) Backend + Frontend Matching service, звітність розбіжностей

8. Ключові файли для імплементації

Backend

backend/essentials/models/goods_receipt.py         — модель документа і рядків
backend/essentials/serializers/transactions.py     — серіалізатори
backend/essentials/views/transactions.py           — GoodsReceiptViewSet (рядки 367-644)
backend/essentials/services/batch_service.py       — receive_batch, reverse_batch_receipt
backend/essentials/services/currency_service.py    — dual-currency conversion
backend/essentials/models/batch.py                 — Batch, ItemWarehouseStock
backend/essentials/models/stock_transaction.py     — StockTransaction
backend/essentials/models/inventory_journal.py     — InventoryJournal
backend/registers/models/inventory_journal.py      — Register-level InventoryJournal

Frontend

frontend/erp/src/components/Essentials/GoodsReceipt/  — всі 7 компонентів
frontend/erp/src/hooks/useDocumentForm.ts              — хук документної форми
frontend/erp/src/components/shared/DocumentFormShell.tsx — shell-обгортка
frontend/erp/src/components/shared/SubtablePanel.tsx   — табличний редактор рядків
frontend/erp/src/api/inventory.ts                      — API виклики
frontend/erp/src/config/essentials.ts                  — конфігурація (goodsReceipts item)