Перейти до основного контенту
База знань · виробничий облік USD41,42 ПДВ20% МЗП8 000

Odoo

Модуль

Документ-орієнтована модель: sale.order → picking → invoice

Як один SO запускає ланцюг операцій: реальний приклад руху бізнес-документа крізь моделі Odoo.

Шлях у меню
Sales / Inventory / Accounting
Технічна модель
sale.order, stock.picking, account.move, account.payment
Редакція
2026-05-28

Що це і навіщо

Одна з найкрасивіших ідей Odoo — документ-орієнтований ланцюг. Підтверджуєш sale.order — і Odoo сам створює stock.picking на відвантаження. Валідуєш picking — створюється account.move (інвойс). Реєструєш платіж — створюється account.payment і одразу автоматично реконсилюється з інвойсом. Один бізнес-документ запускає чотири пов’язані сутності, які знають одна про одну.

Бухгалтер отримує дві речі: повну простежуваність («звідки взявся цей інвойс? — з SO/123, ось він») і автоматичні проводки на кожному кроці без ручної бухгалтерії. Але тут і пастка: якщо хтось у ланцюгу зламається, посиплються всі downstream-документи.

Як це працює

Стандартний прямий потік для матеріальних товарів (без виробництва):

sale.order (Sales)
   ↓ action_confirm()
stock.picking (Outgoing, Inventory)
   ↓ button_validate()
account.move (Customer Invoice, Accounting)
   ↓ action_register_payment()
account.payment (Accounting)
   ↓ reconcile with invoice
[Done]

Кожна стрілка — це або кнопка користувача, або server action.

# Підтвердження SO
so = self.env['sale.order'].browse(so_id)
so.action_confirm()
# Тепер so.picking_ids = [picking1]

# Валідація picking
picking = so.picking_ids[0]
picking.move_ids.quantity = picking.move_ids.product_uom_qty  # підтвердили кількості
picking.button_validate()
# Тепер stock.move у стані done, account.move посилання через stock_valuation_layer_ids

# Створення інвойсу
invoice = so._create_invoices()
invoice.action_post()
# Тепер so.invoice_ids = [invoice], invoice.state = 'posted'

# Реєстрація платежу
payment_wizard = self.env['account.payment.register'].with_context(
    active_model='account.move', active_ids=invoice.ids).create({})
payment_wizard.action_create_payments()
# Тепер account.payment створено і реконсильовано з invoice

Зв’язки між моделями:

  • sale.order.picking_ids (One2many) → stock.picking
  • sale.order.invoice_ids (One2many) → account.move
  • stock.picking.sale_id (Many2one) ← назад до SO
  • account.move.invoice_origin (Char) — зберігає назву SO
  • account.move.line.sale_line_ids (Many2many) → лінії SO
  • account.payment.reconciled_invoice_ids → інвойси, з якими реконсильовано

Шлях для дослідження ланцюга — Smart buttons на формі SO: «Deliveries», «Invoices», «Payments».

У контексті українського обліку

Український ланцюг трохи розкішніший:

sale.order

stock.picking (Outgoing) + ТТН (друк)

account.move (Customer Invoice) + Видаткова накладна (друк)
   ↓ автоматично
tax.invoice.ua (Податкова накладна) → ЄРПН

account.payment
   ↓ Bank Statement reconciliation
[Done]

Між інвойсом і платежем вклинюється податкова накладна: у неї свій термін реєстрації (15 днів від дати першої події) і своє правове життя. Тут грають ще два чинники:

  • Дата першої події. Якщо платіж прийшов раніше за відвантаження — це передоплата, і ПН складають на дату платежу. Тоді ланцюг розгортається у зворотному порядку: account.payment → tax.invoice → stock.picking → account.move.
  • РК при поверненнях. Customer returns → stock.picking (incoming) → account.move (credit note) → tax.invoice.ua (РК зменшуюче).

Як ми робимо (defense-контекст)

ТОВ «УкрПром-Тех» розширила ланцюг defense-сценарієм:

defense.contract (А-1234)
   ↓ generates
sale.order (з prozorro_lot_id, defense_contract_id)
   ↓ підтвердження + перевірка ЄДРПОУ замовника проти ДСЕКУ
mrp.production (з обов'язковим серійним номером)
   ↓ закриття MO
stock.picking + Акт приймання-передачі
   ↓ підпис представника замовника
account.move + Видаткова + ПН (тип 09 — звільнено)

account.payment (часто частковими — за етапами)
   ↓ контракт А-1234 закрито, коли усі лінії оплачені

Усі моделі знають про defense_contract_id. Це поле тягнеться ланцюгом — на SO, на MO, на picking, на invoice. Звітність за контрактом А-1234 = просто всі сутності з цим ID.

У defense є ще зворотний ланцюг для повернень і браку — окремий складний випадок: stock.return.picking → stock.picking (return) → reverse mrp.production (якщо брак виявлено в експлуатації) → account.move credit note → tax.invoice РК.

Типові граблі

  • Розрив ланцюга вручну. Бухгалтер створив інвойс «не з SO» (через меню Accounting → Customer Invoice). У SO smart-button «Invoices» порожній. Звітність за контрактом не сходиться. Правило: завжди створюй downstream через кнопки upstream-документа.
  • Picking валідовано до підтвердження SO. Технічно можливо, якщо вручну створити stock.picking. Тоді SO лишається у draft, а склад уже зменшується. Лікувати доводиться так: підв’язати picking через sale_id заднім числом (працює погано).
  • Invoice без invoice_origin. Інвойс ніби «сам по собі», SO за ним не простежити. У defense-контексті це критично: для аудиту за контрактом А-1234 треба бачити повний ланцюг.
  • Reconciliation не спрацював. Платіж є, інвойс є, але не зв’язані — система покаже інвойс як неоплачений. Часто причина — різні partner_id (платіж від «дочки», інвойс на «маму») або різні валюти без рахунку курсової різниці.
  • Backorder ламає звітність. Замовив 100, відвантажив 80, решта 20 — backorder. Інвойс на 80 (так працює Invoicing policy=delivered quantities), а SO ще показує 100 в очікуванні. Якщо ті 20 так і не відвантажать, backorder висітиме. Закрий його — або відвантаженням, або скасуванням.
  • MO між SO і інвойсом. Make-to-order: SO → MO → MO done → stock.picking → invoice. Якщо MO зависло — нема picking, нема інвойсу. Завжди стеж за статусом MO у smart-button.
  • Скасування не йде ланцюгом назад. Скасував SO — picking лишився у стані assigned, але без джерела. Запас і далі зарезервований. Cron «Stuck pickings» це покаже, але краще до такого не доводити.

Дотичне

  • Як думає Odoo — як такі ланцюги взагалі моделюються
  • Sales — глибше про SO
  • Inventory — глибше про picking
  • Accounting — глибше про invoice і payment
  • ПН/РК — вклинення ПН у ланцюг

Де цей модуль зустрічається7