Хто прадае ў 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
Змест гэтага артыкула
- Праблема: наборы прадуктаў без уплыву на выгляд друку
- Што робіць модуль
- Тры рэжымы друку падрабязна
- Схаваныя радкі і фіксаваная цана
- Тэхнічная рэалізацыя
- Устаноўка без непажаданых пабочных эфектаў
- Работа крок за крокам
- Тэсты
- Абмежаванні і юрыдычная заўвага
- Частыя пытанні
- Падтрымка ад Biricon IT Services
Праблема: наборы прадуктаў без уплыву на выгляд друку
Набор прадуктаў у Odoo — гэта падрыхтаваны спіс прадуктаў з колькасцямі. Замест таго каб шукаць дзесяць артыкулаў паасобку, у прапанове выбіраюць Дадаць набор і адразу атрымліваюць усе радкі. Гэта эканоміць час і не дазваляе забыць пазіцыі. Модуль sale_product_set з рэпазіторыя OCA/sale-workflow робіць менавіта гэта; самі наборы паходзяць з product_set у рэпазіторыі OCA/product-attribute.
На практыцы застаюцца тры прабелы:
- Няма бачнай групоўкі. Радкі набора трапляюць у прапанову як асобныя пазіцыі. Пасля ўстаўкі ўжо не відаць, якія радкі адносяцца адзін да аднаго, а агульная сума набора нідзе не паказана.
- Няма ўплыву на раздрук. Часам кліент павінен бачыць кожную пазіцыю з цаной, часам толькі цану пакета, часам толькі адзін радок. Гэта рашэнне для кожнага набора або кожнага кліента, а не тое, што трэба ўручную наладжваць нанова ў кожнай прапанове.
- Карэкціроўкі цаны выглядаюць непрыгожа. Калі ў пакета павінна быць фіксаваная цана, якая адрозніваецца ад сумы прайскурантных цэн, патрэбны карэкціруючы радок. Ён належыць да разліку, але не абавязкова на папяровы экзэмпляр кліента.
Odoo 19 мае ўсе складнікі, каб вырашыць гэта акуратна: раздзелы і падраздзелы ў радках прапановы з опцыямі Схаваць цэны і Схаваць склад (палі collapse_prices і collapse_composition). Модуль звязвае набор прадуктаў з гэтымі стандартнымі опцыямі, замест таго каб вынаходзіць уласны фармат друку.
Што робіць модуль
- Уставіць набор як раздзел. Памочнік Дадаць набор стварае радок раздзела з назвай набора, напрыклад 2 × Messkit Standard, і размяшчае радкі набора пад ім. Раздзелы ўнутры набора становяцца падраздзеламі.
- Рэжым друку для кожнага набора. У наборы адзін раз вызначаецца, як ён друкуецца. Пры ўстаўцы рэжым можна змяніць у памочніку для гэтага аднаго выпадку.
- Схаваць радкі. Асобныя радкі можна пазначыць як Не друкаваць (у наборы: Схавана). Яны ўлічваюцца ў суме, але не друкуюцца і не паказваюцца ў партале кліента. Пазнака пераходзіць з прапановы ў рахунак.
- Фіксаваная цана для радка набора. Радок набора можа мець фіксаваную цану за адзінку, якая замяняе цану з прайскуранта, напрыклад для схаванай карэкціроўкі цаны.
Без модуля (толькі sale_product_set) | З sale_product_set_layout |
|---|---|
| Радкі набора як асобныя пазіцыі | Радок раздзела з назвай набора, колькасцю ў назве і радкамі пад ім |
| Раздзелы ў наборы застаюцца раздзеламі і разбураюць структуру | Раздзелы ў наборы становяцца падраздзеламі (line_subsection) |
| Выгляд друку заўсёды аднолькавы | Рэжым друку для кожнага набора, змяняецца пры ўстаўцы |
| Карэкціроўка цаны бачная ў прапанове і рахунку | Радок можна схаваць, але ён уваходзіць у суму |
| Цана толькі з прайскуранта | Дадаткова фіксаваная цана для радка набора |
| Макет канчаецца на прапанове | Макет і схаванне дзейнічаюць для прапановы, заказу, рахунку і партала кліента |
Тры рэжымы друку падрабязна
Рэжым друку — гэта поле выбару print_mode у наборы прадуктаў. Пры ўстаўцы ён пераўтвараецца ў два стандартныя пераключальнікі радка раздзела. Для гэтага ў модулі ёсць адно простае для чытання адпаведнасць:
# Druckmodus -> (collapse_prices, collapse_composition) der Abschnittszeile
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 € за набор) і хаваюць яго. У бэкендзе рахунак зразумелы, а на раздрукоўцы стаіць цана пакета.
Важна для разумення: пазнака hide_in_report нічога не змяняе ў разліку. Колькасць, цана, скідка, падатак, сумы і бухгалтарскія праводкі ўтвараюцца без змен стандартным Odoo. Радок адсутнічае толькі ў раздрукоўцы і ў партале.
- У наборы прадуктаў: радок набора з Схавана і дадаткова Фіксаваная цана разам з Цана за адзінку.
- У радках прапановы і рахунку: слупок Не друкаваць (
hide_in_report), таксама для звычайных радкоў без набора. - Перадача: пры стварэнні рахунку з заказу пазнака пераносіцца на радок рахунку. Пры капіраванні прапановы яна захоўваецца.
Тэхнічная рэалізацыя
Модуль наўмысна невялікі: ён пашырае наяўныя мадэлі і перазапісвае толькі тыя месцы, дзе Odoo вырашае, якія радкі друкаваць і як уставіць набор. Новых шаблонаў справаздач, новых мадэляў і ўмяшання ў SQL няма.
Пашыраныя мадэлі і палі
| Мадэль | Дапаўненне |
|---|---|
product.set | Палі insert_as_section (Boolean, па змоўчанні ўключана) і 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. Наш падыход:
- Паверхнева кланаваць абодва рэпазіторыі OCA (
git clone --depth 1 --branch 19.0). - У асобным каталогу праз сімвалічныя спасылкі падключыць толькі два патрэбныя модулі і гэты каталог дадаць у
addons_path, а не ўсе рэпазіторыі цалкам. - Усталяваць на тэставай базе даных і праверыць, якія модулі пры гэтым атрымліваюць статус усталяваны.
- Толькі пасля гэтага ўсталёўваць у прадукцыйным асяроддзі з
--workers=0, каб выклік не канкурыраваў са службай, якая працуе, за HTTP-порт.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init
Новыя палі і віды пасля ўстаноўкі ёсць у базе даных. Калі служба працуе ў шматпрацэсным рэжыме, воркеры падгружаюць змененую рэестру праз сігнал базы даных. Змяненні ў файлах Python модуля ўступаюць у сілу толькі пасля перазапуску службы.
Работа крок за крокам
- Стварыць набор: Продажы → Прадукты → Наборы прадуктаў. Назваць набор, выбраць Уставіць як раздзел і Рэжым друку, ўнесці прадукты з колькасцямі. Карэкціруючыя радкі пазначыць як Схавана і Фіксаваная цана.
- У прапанове: націснуць Дадаць набор, выбраць набор і колькасць. Назву раздзела і рэжым друку можна змяніць перад уставай.
- Схаваць асобныя радкі: у прапанове або рахунку ўключыць слупок Не друкаваць.
- Праверыць: адкрыць папярэдні прагляд або 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. Прыклады даных на малюнках выбраны адвольна, сумы без падатку.