Преминете към съдържание

Odoo 19 Community: продуктови комплекти като раздел в офертата — нашият модул с отворен код Sale Product Set Layout

2 ноември 2026 г. | Odoo 19 Community | Собствена разработка
2 ноември 2026 г. от
Odoo 19 Community: продуктови комплекти като раздел в офертата — нашият модул с отворен код Sale Product Set Layout

Модул Sale Product Set Layout за Odoo 19: продуктови комплекти като раздели в офертите с режим на печат, скрити редове и фиксирана цена

Ако продавате пакети в Odoo — например измервателен уред с принадлежности и монтаж, работно място с хардуер и настройка или пакет за поддръжка с няколко услуги — офертата трябва да изпълнява едновременно две изисквания: съставът трябва да присъства в поръчката, за да знаят складът, техническият екип и фактурирането какво се доставя. В разпечатката за клиента обаче невинаги е необходимо да се показват всяка отделна позиция и нейната цена.

Модулът на OCA sale_product_set позволява с едно щракване да добавяте продуктови комплекти (спецификации за продажба) към оферта. Той обаче не определя изгледа при печат. За тази цел разработихме модула Sale Product Set Layout (sale_product_set_layout). Той е създаден за Odoo 19 и е достъпен безплатно в GitHub като свободен софтуер под лиценза AGPL-3:

github.com/Alex0176/odoo-sale-product-set-layout

Проблемът: продуктови комплекти без контрол върху изгледа при печат

В Odoo продуктовият комплект е предварително подготвен списък с продукти и количества. Вместо да търсите десет артикула поотделно, избирате Добавяне на комплект в офертата и получавате всички редове наведнъж. Това спестява време и предотвратява пропускането на позиции. Модулът sale_product_set от хранилището OCA/sale-workflow прави точно това; самите комплекти идват от product_set в хранилището OCA/product-attribute.

На практика остават три нерешени проблема:

  • Липсва ясно групиране. Редовете на комплекта се добавят като отделни позиции в офертата. След добавянето вече не се вижда кои редове принадлежат към един комплект, а общата му сума не е посочена никъде.
  • Няма контрол върху разпечатката. Понякога клиентът трябва да вижда всяка позиция с цена, понякога — само пакетната цена, а понякога — един-единствен ред. Това трябва да се задава за всеки комплект или клиент, а не да се настройва ръчно във всяка нова оферта.
  • Ценовите корекции не изглеждат добре. Ако пакетът трябва да има фиксирана цена, различна от сбора на каталожните цени, е необходим коригиращ ред. Той трябва да участва в изчислението, но не е задължително да присъства в документа за клиента.

Odoo 19 разполага с необходимото за подредено решение: раздели и подраздели в редовете на офертата с опциите Скриване на цените и Скриване на състава (полетата collapse_prices и collapse_composition). Модулът свързва продуктовия комплект с тези стандартни опции, вместо да въвежда собствен формат за печат.

Какво прави модулът

  1. Добавяне на комплект като раздел. Помощникът Добавяне на комплект създава ред за раздел с името на комплекта, например 2 × Messkit Standard, и поставя редовете на комплекта под него. Разделите в самия комплект се превръщат в подраздели.
  2. Режим на печат за всеки комплект. Начинът на отпечатване се задава веднъж в комплекта. При добавяне режимът може да се промени в помощника само за конкретния случай.
  3. Скриване на редове. Отделни редове могат да бъдат маркирани като Да не се печата (в комплекта: Скрит). Те участват в общата сума, но не се отпечатват и не се показват в клиентския портал. Този признак се пренася от офертата във фактурата.
  4. Фиксирана цена за всеки ред от комплекта. Ред от комплекта може да има фиксирана единична цена, която заменя цената от ценовата листа — например за скрита ценова корекция.
Без модула (само sale_product_set)С sale_product_set_layout
Редовете на комплекта са отделни позицииРед за раздел с името на комплекта и количеството в него, а под него — редовете на комплекта
Разделите в комплекта остават раздели и нарушават структуратаРазделите в комплекта стават подраздели (line_subsection)
Изгледът при печат винаги е един и същРежим на печат за всеки комплект с възможност за промяна при добавяне
Ценовата корекция се вижда в офертата и фактуратаРедът може да се скрие, но продължава да участва в общата сума
Цена само от ценовата листаВъзможност за фиксирана цена за всеки ред от комплекта
Оформлението се прилага само за офертатаОформлението и скриването важат за офертата, поръчката, фактурата и клиентския портал

Трите режима на печат в подробности

Режимът на печат е поле за избор print_mode в продуктовия комплект. При добавяне той задава стойностите на двата стандартни превключвателя в реда на раздела. За това в модула има едно ясно и лесно за четене съответствие:

# Режим на печат -> (collapse_prices, collapse_composition) на реда на раздела
PRINT_MODE_FLAGS = {
    "detailed":   (False, False),
    "total_only": (True,  False),
    "summary":    (False, True),
}

Три изгледа при печат на един и същ продуктов комплект: всеки ред с цена, само обща цена, един-единствен ред

Режим (print_mode)Показване в комплектаСтандартна опция в OdooИзглед при печат
detailedВсеки ред с ценанямаРаздел, всички редове със сума
total_onlyРедове без цени, само обща сума за комплектаcollapse_prices (Скриване на цените)Раздел с обща сума, редове само с наименование и количество
summaryСамо един ред с общата сума за комплектаcollapse_composition (Скриване на състава)Само редът на раздела с общата сума

Тъй като модулът използва стандартните опции на Odoo 19, а не собствен формат за печат, офертите, потвържденията на поръчки, фактурите и клиентският портал работят без промени в шаблоните за отчети. Същото поведение се получава и при собствени шаблони за отчети, които отчитат опциите на разделите.

Скрити редове и фиксирана цена

Често срещан случай: пакетът има обща цена, по-ниска от сбора на отделните цени. Вместо да променяте изкуствено отделните цени, добавяте коригиращ ред с фиксирана цена (например −50,00 € за комплект) и го скривате. В административния интерфейс изчислението остава проследимо, а в разпечатката се вижда пакетната цена.

Корекцията на цената е видима и обозначена в административния интерфейс, но не се показва в разпечатката на офертата, PDF файла, фактурата и портала; общата сума е една и съща

Важно уточнение: отметката hide_in_report не променя изчисленията. Количеството, цената, отстъпката, данъкът, сумите и счетоводните записвания се определят по стандартния начин в Odoo. Редът се скрива само в разпечатката и в портала.

  • В продуктовия комплект: ред от комплекта с Скрит и по избор Фиксирана цена с Единична цена.
  • В редовете на офертите и фактурите: колона Да не се отпечатва (hide_in_report), включително за обикновени редове извън комплект.
  • Прехвърляне: при създаване на фактура от поръчката отметката се прехвърля в реда на фактурата. При копиране на оферта тя се запазва.

Техническа реализация

Модулът е умишлено малък: разширява съществуващи модели и предефинира само местата, в които Odoo определя кои редове да се отпечатват и как да се вмъква комплект. Няма нови шаблони за отчети, нови модели или намеси чрез SQL.

Поток на данните: продуктов комплект, помощник за добавяне на комплект, редове на офертата, редове на фактурата, PDF и клиентски портал

Разширени модели и полета

МоделДопълнение
product.setПолета insert_as_section (булево, включено по подразбиране) и print_mode (избор, задължително поле, стойност по подразбиране detailed)
product.set.lineПолета hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() ги предава на реда на офертата
sale.product.set.wizardПолета insert_as_section, section_name, print_mode; _prepare_order_lines() създава реда за раздела
sale.order / sale.order.lineПоле hide_in_report (с copy=True); _get_order_lines_to_report() пропуска скритите редове; _prepare_invoice_line() предава отметката
account.move / account.move.lineПоле hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() и _get_subsection_lines() филтрират скритите редове; _get_aml_vals() добавя отметката към данните за отчета

Скриване в офертата

Odoo 19 определя редовете на офертата за отпечатване в _get_order_lines_to_report(). Модулът добавя филтър на това място. Не е необходима промяна в шаблона за отчета: както шаблонът за отчета на офертата (sale/report/ir_actions_report_templates.xml), така и шаблонът за портала (sale/views/sale_portal_templates.xml) извикват този метод:

class SaleOrder(models.Model):
    _inherit = "sale.order"

    def _get_order_lines_to_report(self):
        return super()._get_order_lines_to_report().filtered(
            lambda line: not line.hide_in_report
        )

Прехвърляне към фактурата

При създаване на реда на фактурата от реда на офертата (_prepare_invoice_line()) отметката се копира. При фактурите в Odoo 19 редовете попадат в отчета по няколко пътя: чрез основния списък, редовете на раздел и редовете на подраздел. Затова модулът филтрира на няколко места (_get_move_lines_to_report() в документа, _get_detailed_lines() и _get_subsection_lines() в реда), иначе скрит ред може да се появи отново по някой от другите пътища:

class AccountMoveLine(models.Model):
    _inherit = "account.move.line"

    hide_in_report = fields.Boolean(string="Hidden in report")

    def _get_aml_vals(self, hide_taxes):
        res = super()._get_aml_vals(hide_taxes)
        res["hide_in_report"] = self.hide_in_report
        return res

    def _get_detailed_lines(self, children_lines, direct_children_lines, subsection_lines):
        res = super()._get_detailed_lines(children_lines, direct_children_lines, subsection_lines)
        return [vals for vals in res if not vals.get("hide_in_report")]

    def _get_subsection_lines(self, subsection_line, children_lines):
        res = super()._get_subsection_lines(subsection_line, children_lines)
        return [vals for vals in res if not vals.get("hide_in_report")]

Помощникът: раздел вместо отделни редове

Помощникът sale.product.set.wizard от sale_product_set създава редовете на офертата в _prepare_order_lines(). Модулът предефинира този метод. Ако Вмъкване като раздел е изключено, той просто извиква оригиналния метод и поведението остава като в модула на OCA. В противен случай първо се създава редът за раздела с двете стандартни опции, а след него следват редовете на комплекта. Разделите вътре в комплекта се превръщат в подраздели, за да не нарушават основната структура:

def _prepare_order_lines(self):
    if not self.insert_as_section:
        return super()._prepare_order_lines()
    sequence = self._get_max_sequence() + 1
    collapse_prices, collapse_composition = PRINT_MODE_FLAGS[self.print_mode or "detailed"]
    order_lines = [Command.create({
        "display_type": "line_section",
        "name": self.section_name or self.product_set_id.name,
        "sequence": sequence,
        "collapse_prices": collapse_prices,
        "collapse_composition": collapse_composition,
    })]
    for set_line in self._get_lines():
        sequence += 1
        values = set_line.prepare_sale_order_line_values(self.order_id, self.quantity, sequence)
        if values.get("display_type") == "line_section":
            values["display_type"] = "line_subsection"
        order_lines.append(Command.create(values))
    return order_lines

Решения, които остават незабелязани

  • Отделни методи за изчисление в помощника. Името на раздела, режимът на печат и Вмъкване като раздел имат отделни compute методи (store=True, readonly=False). Ако и трите използваха един и същ метод, изрично подадената стойност за едно поле би нулирала останалите. Това се забелязва най-вече когато помощникът се създава със зададени стойности чрез код или в тестове.
  • Име на раздела с количество. При количество, различно от 1, то се включва в името (2 × Messkit Standard, със запетая като десетичен разделител), за да е ясно в разпечатката, че става дума за два комплекта, а не за един комплект с двойно количество.
  • Фиксирана цена само за продуктови редове. prepare_sale_order_line_values() задава hide_in_report и price_unit само ако редът от комплекта не е раздел или бележка.
  • Стандартните възможности на Odoo вместо собствена реализация. Режимът на печат и скриването използват съществуващите точки за разширение и полета на Odoo 19. Това улеснява актуализациите: ако Odoo промени шаблона за отчета, модулът продължава да работи, докато методите са налични.

Инсталиране без нежелани странични ефекти

Изисквания: Odoo 19.0, sale_product_set (OCA/sale-workflow, клон 19.0) и product_set (OCA/product-attribute, клон 19.0). Самият модул зависи от sale_management, sale_product_set и account.

Капанът на auto_install при модулите на OCA. Много хранилища на OCA съдържат модули с auto_install. Щом зависимостите им са налични, Odoo ги инсталира автоматично при следващото инсталиране или актуализиране, без да пита. Затова, ако добавите цялото хранилище на OCA в addons_path на работеща производствена инсталация, може да получите модули, които никой не е искал. При OCA/product-attribute това засяга например purchase_product_template_tags. Нашият подход:

  1. Клонирайте двете хранилища на OCA с ограничена дълбочина (git clone --depth 1 --branch 19.0).
  2. В отделна директория създайте символни връзки само към двата необходими модула и добавете тази директория в addons_path, а не целите хранилища.
  3. Инсталирайте в тестова база данни и проверете кои модули получават статус Инсталиран.
  4. Едва след това инсталирайте в производствената среда с --workers=0, за да не се конкурира командата с работещата услуга за HTTP порта.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init

След инсталирането новите полета и изгледи са в базата данни. Ако услугата работи с няколко процеса, работните процеси зареждат променения регистър чрез сигнал от базата данни. Промените в Python файловете на модула влизат в сила едва след рестартиране на услугата.

Работа стъпка по стъпка

  1. Създаване на комплект: Продажби → Продукти → Продуктови комплекти. Задайте име на комплекта, изберете Вмъкване като раздел и Режим на печат, въведете продуктите и количествата. За коригиращите редове включете Скрит и Фиксирана цена.
  2. В офертата: щракнете върху Добавяне на комплект и изберете комплект и количество. Името на раздела и режимът на печат могат да се променят преди вмъкването.
  3. Скриване на отделни редове: в офертата или фактурата поставете отметка в колоната Да не се отпечатва.
  4. Проверка: отворете предварителния преглед или PDF файла. Клиентите виждат същото оформление в портала.

Тестове

Модулът включва шест автоматизирани теста, които изпълняваме върху тестова база данни преди внедряване в производствена среда:

  • Вмъкване като раздел, включително режим на печат и подраздел
  • Скритият ред липсва сред редовете за отпечатване
  • Режим Един ред (summary)
  • Вмъкване без раздел — първоначалното поведение
  • Фактурата наследява отметката hide_in_report, редът липсва в _get_move_lines_to_report(), но сумата на раздела продължава да го включва
  • Фактурата в режим Само обща цена не показва скрития ред сред редовете на раздела, а сумата на раздела остава пълна
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
         --test-enable --test-tags=/sale_product_set_layout --stop-after-init

Преводът на немски се намира в i18n/de.po.

Ограничения и правна бележка

  • Само оформление. Модулът не променя начина, по който Odoo изчислява цени, отстъпки или данъци. Променя само това, което се отпечатва и показва в портала.
  • Скритите редове се виждат в административния интерфейс. Отметката засяга разпечатката и портала, а не изгледа на екрана на служителите.
  • Проверете данните във фактурата. Скритите редове участват в общата сума, но не се показват в документа. Дали това отговаря на изискванията за фактурите във Вашата държава, например за задължителното описание на доставените стоки или извършените услуги, трябва да уточните с Вашия данъчен консултант преди да използвате функцията във фактури. При офертите скриването е по-малко проблематично. Статията не представлява правна консултация.
  • Тестван за Odoo 19. Разработихме и тествахме модула за Odoo 19.0 Community. Той използва опции за разделите, които са налични в този вид в Odoo 19. Не сме го проверявали с други версии.

Често задавани въпроси

Защо ми е нужен модулът, след като Odoo 19 вече поддържа раздели?

Опциите за разделите са част от самия Odoo. Липсваше връзката с продуктовия комплект: комплектът трябва да се вмъква автоматично като раздел с режима на печат, зададен в него. Без модула след всяко вмъкване трябва ръчно да създавате и настройвате реда за раздела.

Каква е разликата между „Скриване на цените“ и „Скриване на състава“?

„Скриване на цените“ (collapse_prices) запазва редовете, но не показва сумите им, а само общата сума за раздела. „Скриване на състава“ (collapse_composition) скрива редовете изцяло и клиентът вижда само раздела с общата сума.

Участва ли скритият ред в общата сума на фактурата?

Да. Скритите редове продължават да участват в общата сума, данъка и осчетоводяването. Отметката засяга само показването в разпечатката и в клиентския портал.

Мога ли да променя режима на печат при вмъкването?

Да. Режимът се взема по подразбиране от комплекта, но в помощника може да се промени за конкретното вмъкване. Самият комплект остава непроменен.

Какво се случва, ако изключа Вмъкване като раздел?

Тогава помощникът работи както в модула на OCA sale_product_set: редовете на комплекта се вмъкват без раздел.

Инсталира ли модулът неочаквано допълнителни модули?

Самият модул — не. При хранилищата на OCA обаче може да се инсталират модули с auto_install, ако добавите цели хранилища в addons_path. Затова създайте връзки само към двата необходими модула (вижте раздела за инсталиране).

Работи ли модулът с Odoo Enterprise?

Модулът зависи само от модули на Community (sale_management, account) и от двата модула на OCA. Използваме го с Odoo 19 Community. Не сме го тествали с Enterprise.

Под какъв лиценз се разпространява модулът?

Под AGPL-3. Можете да го използвате, променяте и разпространявате, стига промените да останат под същия лиценз. Сигнали за грешки и предложения за подобрения можете да подавате като Issue в хранилището в GitHub — ще се радваме да ги получим.

Съдействие от Biricon IT Services

Поддържаме Odoo инсталации за малки и средни предприятия: от внедряване през персонализиране и миграции до ежедневна експлоатация. Ако искате да адаптирате продуктови комплекти, шаблони за оферти или отчети в Odoo 19 към работните си процеси или да проверите модул като този и да го внедрите надеждно в производствената си среда, с удоволствие ще Ви помогнем. Още наши разработки ще намерите в блога ни, например Обединяване на чернови на фактури и Автоматично съпоставяне на банкови плащания с фактури.

Свържете се с нас

Актуално към: 2 ноември 2026 г. Версия на модула 19.0.1.0.0. Примерните данни в изображенията са произволно избрани, сумите са без ДДС.