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

Odoo

Модуль

QWeb звіти — друковані форми у твоїх руках

Як Odoo рендерить PDF через QWeb: синтаксис шаблонів, директиви t-set/t-if/t-foreach, кастомізація ПН з типом причини 09 і defense-смугою.

Шлях у меню
Налаштування → Технічні → Звіти QWeb
Технічна модель
ir.actions.report, ir.ui.view (type=qweb)
Редакція
2026-05-28

Що це і навіщо

QWeb — це шаблонний рушій Odoo для PDF, HTML і навіть фронтенду. Треба роздрукувати інвойс, скласти ТТН, згенерувати акт виконаних робіт чи сертифікат відповідності — усе йде через QWeb. На вигляд це звичайний XML з директивами t-*. По суті — Python, обмежений до безпечної підмножини, бо рендериться у sandbox.

QWeb треба знати, бо стандартні форми Odoo для України не годяться: інвойс не схожий на видаткову накладну, delivery slip — на ТТН, sales report — на акт. Свої форми тобі доведеться писати або переробляти чужі. Або купити Oblik-ERP, де основні вже готові, — але defense-специфіка все одно лишається на тобі.

Як це працює

Чотири складники:

  1. ir.actions.report — «дія» друку. Прив’язана до моделі, посилається на шаблон.
  2. ir.ui.view із type='qweb' — сам шаблон.
  3. web_layout / external_layout — обгортка з шапкою і футером компанії: логотип, адреса, контакти.
  4. wkhtmltopdf або chromium — рендерер у PDF.

Базовий шаблон:

<template id="report_my_doc">
  <t t-call="web.html_container">
    <t t-foreach="docs" t-as="o">
      <t t-call="web.external_layout">
        <div class="page">
          <h2>Документ № <span t-field="o.name"/></h2>
          <p>Дата: <span t-field="o.date" t-options="{'widget': 'date'}"/></p>
          <p>Партнер: <span t-field="o.partner_id"/></p>

          <table class="table table-sm">
            <thead>
              <tr><th>Позиція</th><th>К-сть</th><th>Сума</th></tr>
            </thead>
            <tbody>
              <tr t-foreach="o.line_ids" t-as="line">
                <td t-esc="line.name"/>
                <td t-esc="line.quantity"/>
                <td t-esc="line.amount"
                    t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
              </tr>
            </tbody>
          </table>

          <t t-if="o.is_export">
            <p><strong>Експортна операція. Ставка ПДВ — 0%.</strong></p>
          </t>

          <p>Підпис: ________________ <t t-esc="o.signer_name or '____________'"/></p>
        </div>
      </t>
    </t>
  </t>
</template>

Основні директиви:

  • t-call — вкласти інший шаблон (web.html_container, web.external_layout).
  • t-foreach="lst" t-as="x" — цикл за списком.
  • t-if / t-elif / t-else — умовне відображення.
  • t-set name="varname" value="..." — оголосити змінну.
  • t-esc — виведення з екрануванням HTML (рекомендоване).
  • t-raw — без екранування (лише коли точно знаєш, що це безпечно).
  • t-field — вивести поле моделі з форматуванням за типом.
  • t-options — налаштування форматування (widget, display_currency, locale).
  • t-att-* — динамічні атрибути HTML (t-att-class="'red' if o.overdue else 'green'").

Прив’язка до action:

<record id="action_report_my_doc" model="ir.actions.report">
  <field name="name">My Custom Report</field>
  <field name="model">my.model</field>
  <field name="report_type">qweb-pdf</field>
  <field name="report_name">my_module.report_my_doc</field>
  <field name="report_file">my_module.report_my_doc</field>
  <field name="binding_model_id" ref="model_my_model"/>
  <field name="binding_type">report</field>
</record>

Де шукати: Settings → Technical → Reports (усі actions) і → User Interface → Views (з фільтром type=qweb).

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

Що зазвичай збираємо у QWeb для України:

  • Видаткова накладна. Стандартний invoice переписуємо в український формат: номер, дата, ким відпущено, ким отримано, ким перевірено, печатка.
  • ТТН. З марками авто, прізвищем водія, маршрутом і пунктом призначення.
  • Акт виконаних робіт. Як видаткова, але з описом послуг.
  • Касова книга. Зведення ПКО/ВКО за день із підсумками.
  • Інвентаризаційна відомість. Список ТМЦ з фактичними й обліковими кількостями, ким перевірено.
  • Бухгалтерська довідка. Для ручних коригувань: обґрунтування, проводка, підпис головбуха.
  • ПН/РК для друку. ПН реєструється в ЄРПН, але контрагентові потрібна й друкована копія. Формат — за наказом Мінфіну № 1307.

Усі ці форми мають підписи — посади, прізвища, печатку. Шаблон це враховує: або тягне з моделі (signer_id.name, signer_id.position), або підставляє дані компанії за замовчуванням.

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

ТОВ «УкрПром-Тех» зібрала власний модуль kit_blanks_def:

  • М-11 з defense-смугою. До стандартної М-11 додає чорну смугу зверху з написом «ОБЛІК ВТП», номер контракту А-XXXX і підпис представника замовника.
  • Сертифікат відповідності виробу. Власна форма. На вході — mrp.production (закрите МО). Виводить номер партії, серійники компонентів зі stock.lot, результати тестування (з кастомного qc.test.result), підписи QC-офіцера й керівника виробництва.
  • EUC (End-User Certificate). Для експорту. На вході — окремий документ export.declaration. Виводить дані кінцевого користувача, призначення, заборону реекспорту, посилання на дозвіл ДСЕКУ. Двома мовами — UA і EN.
  • Калькуляція собівартості. На основі МО та analytic_lines. Виводить прямі матеріали за BoM, прямі зарплати з таймшитів, ЗВВ за коефіцієнтом і повну виробничу собівартість.

Усі ці форми викликаються кнопками на формах відповідних моделей. Якщо defense-контекст активний (поле is_defense=True на документі), Odoo пропонує defense-варіант форми.

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

  • wkhtmltopdf не вміє CSS3. Стандартний Odoo рендерить через wkhtmltopdf 0.12.x. Він не знає flex, grid, нових селекторів. Якщо шаблон спирається на display: grid, у PDF буде криво. Перевіряй сам PDF, а не лише HTML-прев’ю. Або переходь на рендерер chromium-pdf.
  • Кирилиця перетворюється на квадратики. Стандартний Helvetica кирилиці не має. Підключи шрифт через CSS і пропиши його в layout. Найнадійніші для PDF — DejaVu Sans, Roboto, Inter.
  • t-field для Char без обробки. Порожнє поле дасть None. Краще t-esc="o.field or ''".
  • t-raw з користувацьким вводом. Це XSS-діра. Якщо поле редагує користувач — тільки t-esc.
  • Розрив сторінки в небажаному місці. Таблиця ламається на півряду. Лікується через CSS: tr { page-break-inside: avoid }. А для примусового розриву — <div style="page-break-before: always">.
  • Шаблон не оновився. Після правки й Update App у кеші лишається стара версія. Перезавантаж модуль (-u my_module --stop-after-init) або скинь веб-кеш через Settings → Translations → Apply (такий собі трюк).
  • PDF величезний, рендериться 30 секунд. Причина одна з трьох: тисячі записів в одному документі (тоді розбивай на сторінки), CSS, що тягне за собою купу reflow, або важкі картинки. Стискай PNG до 400–800 px, прибирай заглушки.
  • external_layout не показав логотип. Або не заповнено company.logo, або layout перебито кастомізацією. Перевір Settings → Companies → External Report Layout.

Дотичне

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