Přejít na obsah

Odoo 19 Community: produktové sady jako sekce v nabídce – náš open source modul Sale Product Set Layout

2. listopadu 2026 | Odoo 19 Community | Vlastní vývoj
2. listopadu 2026 od
Odoo 19 Community: produktové sady jako sekce v nabídce – náš open source modul Sale Product Set Layout

Modul Sale Product Set Layout pro Odoo 19: produktové sady jako sekce v nabídce s režimem tisku, skrytými řádky a pevnou cenou

Kdo v Odoo prodává balíčky, například měřicí přístroj s příslušenstvím a montáží, pracovní stanici s hardwarem a zprovozněním nebo balíček údržby zahrnující několik služeb, potřebuje v nabídce dvě věci současně: složení balíčku musí být uvedeno v objednávce, aby sklad, technici a fakturace věděli, co se dodává. Na výtisku pro zákazníka však nemusí být vždy uvedena každá jednotlivá položka s vlastní cenou.

Modul OCA sale_product_set umožňuje jedním kliknutím vložit do nabídky produktové sady (prodejní kusovníky). Podobu tiskového výstupu ale neřeší. Proto jsme vyvinuli modul Sale Product Set Layout (sale_product_set_layout). Je určen pro Odoo 19 a jako svobodný software pod licencí AGPL-3 je zdarma dostupný na GitHub:

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

Problém: produktové sady bez možnosti ovlivnit tiskový výstup

Produktová sada je v Odoo předem připravený seznam produktů s množstvím. Místo hledání deseti položek jednotlivě stačí v nabídce zvolit Přidat sadu a všechny řádky se vloží najednou. Šetří to čas a předchází vynechání položek. Modul sale_product_set z repozitáře OCA/sale-workflow dělá právě toto; samotné sady pocházejí z modulu product_set v repozitáři OCA/product-attribute.

V praxi ale zůstávají tři nedostatky:

  • Chybí zřetelné seskupení. Řádky sady se do nabídky vloží jako samostatné položky. Po vložení již není vidět, které řádky patří k sobě, a celková částka za sadu není nikde uvedena.
  • Nelze ovlivnit tiskový výstup. Někdy má zákazník vidět každou položku s cenou, jindy pouze cenu balíčku a jindy jediný řádek. To se má nastavit pro konkrétní sadu nebo zákazníka, nikoli pokaždé ručně upravovat v každé nabídce.
  • Úpravy cen nevypadají dobře. Pokud má mít balíček pevnou cenu, která se liší od součtu ceníkových cen, je potřeba vyrovnávací řádek. Ten patří do výpočtu, ale nemusí nutně patřit na výtisk pro zákazníka.

Odoo 19 nabízí potřebné prvky pro přehledné řešení: sekce a podsekce v řádcích nabídky s možnostmi Skrýt ceny a Skrýt složení (pole collapse_prices a collapse_composition). Modul propojuje produktovou sadu s těmito standardními možnostmi, místo aby zaváděl vlastní formát tisku.

Co modul umí

  1. Vložení sady jako sekce. Průvodce Přidat sadu vytvoří řádek sekce s názvem sady, například 2 × Měřicí sada Standard, a pod něj umístí řádky sady. Sekce uvnitř sady se změní na podsekce.
  2. Režim tisku pro každou sadu. U sady se jednou nastaví, jak se má tisknout. Při vkládání lze režim v průvodci pro daný případ změnit.
  3. Skrytí řádků. Jednotlivé řádky lze označit jako Netisknout (v sadě: Skryté). Započítávají se do celkové částky, ale netisknou se ani se nezobrazují v zákaznickém portálu. Toto označení se přenáší z nabídky na fakturu.
  4. Pevná cena pro každý řádek sady. Řádek sady může mít pevnou jednotkovou cenu, která nahradí cenu z ceníku, například pro skrytou úpravu ceny.
Bez modulu (pouze sale_product_set)S modulem sale_product_set_layout
Řádky sady jako samostatné položkyŘádek sekce s názvem sady a množstvím v názvu, položky pod ním
Sekce v sadě zůstávají sekcemi a narušují strukturuSekce v sadě se mění na podsekce (line_subsection)
Tiskový výstup je vždy stejnýRežim tisku pro každou sadu, který lze při vložení změnit
Úprava ceny je viditelná v nabídce i na faktuřeŘádek lze skrýt, přesto se započítává do celkové částky
Cena pouze z ceníkuVolitelná pevná cena pro každý řádek sady
Rozvržení platí jen pro nabídkuRozvržení a skrytí platí pro nabídku, objednávku, fakturu i zákaznický portál

Tři režimy tisku podrobně

Režim tisku se nastavuje ve výběrovém poli print_mode u produktové sady. Při vložení se převede na nastavení dvou standardních přepínačů řádku sekce. Modul k tomu používá jediné, snadno čitelné mapování:

# Režim tisku -> (collapse_prices, collapse_composition) řádku sekce
PRINT_MODE_FLAGS = {
    "detailed":   (False, False),
    "total_only": (True,  False),
    "summary":    (False, True),
}

Tři podoby tiskového výstupu stejné produktové sady: každý řádek s cenou, pouze celková cena, jediný řádek

Režim (print_mode)Označení v saděStandardní možnost v OdooTiskový výstup
detailedKaždý řádek s cenoužádnáSekce, všechny řádky s částkou
total_onlyŘádky bez cen, pouze celková cena sadycollapse_prices (Skrýt ceny)Sekce s celkovou částkou, řádky pouze s názvem a množstvím
summaryPouze jeden řádek s celkovou cenou sadycollapse_composition (Skrýt složení)Pouze řádek sekce s celkovou částkou

Protože modul používá standardní možnosti Odoo 19 a nemá vlastní tiskový formát, fungují nabídky, potvrzení objednávek, faktury i zákaznický portál bez úprav šablon sestav. Stejné chování získáte i s vlastními šablonami sestav, pokud zohledňují nastavení sekcí.

Skryté řádky a pevná cena

Častý případ: balíček má cenu nižší než součet cen jednotlivých položek. Místo úprav jednotlivých cen použijete korekční řádek s pevnou cenou (například −50,00 € za sadu) a skryjete ho. V administraci je výpočet přehledný, na výtisku se zobrazí cena balíčku.

Cenová korekce je v administraci viditelná a označená, na výtisku nabídky, v PDF, na faktuře ani na portálu se nezobrazuje, celková částka zůstává stejná

Důležité pro pochopení: příznak hide_in_report nijak nemění výpočet. Množství, cenu, slevu, daň, součty i účetní zápisy nadále zpracovává standardní Odoo beze změny. Řádek se pouze nezobrazuje na výtisku a na portálu.

  • U produktové sady: řádek sady s volbou Skryté a volitelně s volbou Pevná cena a hodnotou Jednotková cena.
  • Na řádcích nabídek a faktur: sloupec Netisknout (hide_in_report), a to i pro běžné řádky bez sady.
  • Přenos: při vytvoření faktury z objednávky se příznak přenese na řádek faktury. Při kopírování nabídky zůstává zachován.

Technické řešení

Modul je záměrně malý: rozšiřuje stávající modely a přepisuje pouze místa, kde Odoo rozhoduje, které řádky se vytisknou a jak se vloží sada. Neobsahuje nové šablony sestav, nové modely ani zásahy do SQL.

Tok dat: produktová sada, průvodce přidáním sady, řádky nabídky, řádky faktury, PDF a zákaznický portál

Rozšířené modely a pole

ModelRozšíření
product.setPole insert_as_section (logická hodnota, ve výchozím nastavení zapnuto) a print_mode (výběr, povinné pole, výchozí hodnota detailed)
product.set.linePole hide_in_report, fixed_price, price_unit; metoda prepare_sale_order_line_values() je předává řádku nabídky
sale.product.set.wizardPole insert_as_section, section_name, print_mode; metoda _prepare_order_lines() vytváří řádek sekce
sale.order / sale.order.linePole hide_in_report (s copy=True); metoda _get_order_lines_to_report() vynechává skryté řádky; metoda _prepare_invoice_line() předává příznak dál
account.move / account.move.linePole hide_in_report; metody _get_move_lines_to_report(), _get_detailed_lines() a _get_subsection_lines() odfiltrují skryté řádky; metoda _get_aml_vals() přidává příznak do dat sestavy

Skrývání řádků v nabídce

Odoo 19 určuje řádky nabídky určené k tisku v metodě _get_order_lines_to_report(). Modul do ní přidává filtr. Zásah do šablony sestavy není nutný: tuto metodu volá jak šablona sestavy nabídky (sale/report/ir_actions_report_templates.xml), tak šablona portálu (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
        )

Přenos na fakturu

Při vytváření řádku faktury z řádku nabídky (_prepare_invoice_line()) se příznak zkopíruje. U faktur v Odoo 19 existuje několik cest, kterými se řádky dostávají do sestavy: hlavní seznam, řádky sekce a řádky podsekce. Modul proto filtruje na několika místech (_get_move_lines_to_report() na dokladu, _get_detailed_lines() a _get_subsection_lines() na řádku), jinak by se skrytý řádek mohl některou z ostatních cest znovu objevit:

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")]

Průvodce: sekce místo samostatných řádků

Průvodce sale.product.set.wizard z modulu sale_product_set vytváří řádky nabídky v metodě _prepare_order_lines(). Modul tuto metodu přepisuje. Pokud je volba Vložit jako sekci vypnutá, jednoduše zavolá původní metodu a chování zůstane stejné jako v modulu OCA. Jinak nejprve vznikne řádek sekce se dvěma standardními přepínači a za ním následují řádky sady. Sekce uvnitř sady se změní na podsekce, aby nenarušovaly hlavní strukturu:

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

Rozhodnutí, která nejsou na první pohled vidět

  • Samostatné výpočetní metody v průvodci. Název sekce, režim tisku a volba Vložit jako sekci mají každý vlastní metodu compute (store=True, readonly=False). Kdyby všechny tři používaly stejnou metodu, explicitně předaná hodnota jednoho pole by resetovala ostatní. To se projeví zejména tehdy, když se průvodce vytváří s předanými hodnotami z kódu nebo v testech.
  • Název sekce s množstvím. Pokud se množství nerovná 1, uvede se v názvu (2 × Messkit Standard, s čárkou jako desetinným oddělovačem), aby bylo z výtisku patrné, že jde o dvě sady, nikoli o jednu sadu s dvojnásobným množstvím.
  • Pevná cena pouze u produktových řádků. Metoda prepare_sale_order_line_values() nastavuje hide_in_report a price_unit pouze tehdy, pokud řádek sady není řádkem sekce ani poznámky.
  • Standardní Odoo místo vlastního řešení. Režim tisku a skrývání využívají stávající rozšiřující body a pole Odoo 19. Díky tomu jsou aktualizace snáze zvládnutelné: když Odoo změní šablonu sestavy, modul zůstane funkční, dokud zůstanou zachované příslušné metody.

Instalace bez nežádoucích vedlejších účinků

Požadavky: Odoo 19.0, sale_product_set (OCA/sale-workflow, větev 19.0) a product_set (OCA/product-attribute, větev 19.0). Samotný modul závisí na sale_management, sale_product_set a account.

Úskalí auto_install u modulů OCA. Mnoho repozitářů OCA obsahuje moduly s auto_install. Jakmile jsou k dispozici jejich závislosti, Odoo je při příští instalaci nebo aktualizaci automaticky nainstaluje také. Pokud tedy do addons_path produkční instance přidáte celý repozitář OCA, můžete získat i moduly, které nikdo nechtěl. U OCA/product-attribute se to týká například modulu purchase_product_template_tags. Náš postup:

  1. Vytvořte mělké klony obou repozitářů OCA (git clone --depth 1 --branch 19.0).
  2. V samostatném adresáři vytvořte symbolické odkazy pouze na dva potřebné moduly a do addons_path přidejte tento adresář, nikoli celé repozitáře.
  3. Proveďte instalaci do testovací databáze a zkontrolujte, které moduly při ní získají stav nainstalováno.
  4. Teprve poté proveďte instalaci do produkčního prostředí, s parametrem --workers=0, aby příkaz nekolidoval s běžící službou při používání portu HTTP.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init

Po instalaci jsou nová pole a pohledy v databázi. Pokud služba běží ve více procesech, pracovní procesy načtou změněný registr na základě signálu z databáze. Změny v souborech Pythonu daného modulu se projeví až po restartování služby.

Použití krok za krokem

  1. Vytvoření sady: Prodej → Produkty → Produktové sady. Pojmenujte sadu, nastavte volby Vložit jako sekci a Režim tisku a zadejte produkty s množstvím. U korekčních řádků nastavte Skryté a Pevná cena.
  2. V nabídce: klikněte na Přidat sadu, vyberte sadu a množství. Název sekce a režim tisku lze před vložením změnit.
  3. Skrytí jednotlivých řádků: v nabídce nebo na faktuře zaškrtněte u příslušných řádků sloupec Netisknout.
  4. Kontrola: otevřete náhled nebo PDF. Stejné zobrazení uvidí zákazníci na portálu.

Testy

Modul obsahuje šest automatizovaných testů, které spouštíme nad testovací databází před každým nasazením do produkce:

  • Vložení jako sekce včetně režimu tisku a podsekce
  • Skrytý řádek chybí mezi řádky určenými k tisku
  • Režim Jeden řádek (summary)
  • Vložení bez sekce, původní chování
  • Faktura přebírá příznak hide_in_report, řádek chybí ve výsledku _get_move_lines_to_report(), ale součet sekce ho nadále zahrnuje
  • Faktura v režimu Pouze celková cena nezobrazuje skrytý řádek mezi řádky sekce, součet sekce zůstává úplný
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
         --test-enable --test-tags=/sale_product_set_layout --stop-after-init

Německý překlad je v souboru i18n/de.po.

Omezení a právní upozornění

  • Pouze vzhled. Modul nemění způsob, jakým Odoo počítá ceny, slevy nebo daně. Mění pouze to, co se tiskne a zobrazuje na portálu.
  • Skryté řádky jsou v administraci viditelné. Příznak se týká výtisku a portálu, nikoli zobrazení na obrazovce pracovníků.
  • Zkontrolujte údaje na fakturách. Skryté řádky se započítávají do celkové částky, ale na dokladu nejsou uvedeny. Zda je to v souladu s požadavky na faktury ve vaší zemi, například s povinným popisem dodaného zboží nebo poskytnutých služeb, byste měli před použitím na fakturách ověřit se svým daňovým poradcem. U nabídek je skrývání méně problematické. Tento článek není právním poradenstvím.
  • Testováno pro Odoo 19. Modul jsme vyvinuli a otestovali pro Odoo 19.0 Community. Využívá možnosti nastavení sekcí, které jsou v této podobě dostupné v Odoo 19. Pro jiné verze jsme ho neověřovali.

Časté dotazy

K čemu potřebuji tento modul, když Odoo 19 už sekce podporuje?

Možnosti nastavení sekcí jsou součástí samotného Odoo. Chybělo propojení s produktovou sadou: sada se měla automaticky vkládat jako sekce s režimem tisku nastaveným v sadě. Bez modulu je nutné po každém vložení ručně vytvořit a nastavit řádek sekce.

Jaký je rozdíl mezi volbami „Skrýt ceny“ a „Skrýt složení“?

Volba „Skrýt ceny“ (collapse_prices) ponechá řádky zobrazené, ale bez částek; zobrazí se pouze součet u sekce. Volba „Skrýt složení“ (collapse_composition) řádky zcela skryje a zákazník vidí pouze sekci s celkovou částkou.

Započítává se skrytý řádek do celkové částky faktury?

Ano. Skryté řádky zůstávají zahrnuté v celkové částce, dani i zaúčtování. Příznak ovlivňuje pouze zobrazení na výtisku a v zákaznickém portálu.

Mohu při vkládání ještě změnit režim tisku?

Ano. Režim se předvyplní ze sady a v průvodci ho lze pro konkrétní vložení změnit. Samotná sada přitom zůstane beze změny.

Co se stane, když vypnu volbu Vložit jako sekci?

Průvodce se pak chová stejně jako v modulu OCA sale_product_set: řádky sady se vloží bez sekce.

Nainstaluje modul neočekávaně další moduly?

Samotný modul ne. U repozitářů OCA ale může dojít k instalaci modulů s auto_install, pokud do addons_path přidáte celé repozitáře. Proto vytvořte odkazy pouze na dva potřebné moduly (viz část Instalace).

Funguje modul s Odoo Enterprise?

Modul závisí pouze na modulech Community (sale_management, account) a dvou modulech OCA. Používáme ho v Odoo 19 Community. V Enterprise jsme ho netestovali.

Pod jakou licencí je modul dostupný?

Pod licencí AGPL-3. Můžete ho používat, upravovat a dále šířit, pokud změny zůstanou pod stejnou licencí. Hlášení chyb a návrhy na vylepšení formou issue v repozitáři na GitHubu vítáme.

Podpora od Biricon IT Services

Spravujeme instalace Odoo pro malé a střední podniky: od zavedení přes úpravy a migrace až po každodenní provoz. Pokud chcete v Odoo 19 přizpůsobit produktové sady, šablony nabídek nebo sestavy svým pracovním postupům, případně prověřit modul, jako je tento, a bezpečně ho nasadit do produkční instance, rádi vám pomůžeme. Další vlastní řešení najdete na našem blogu, například Sloučení návrhů faktur a Automatické přiřazování bankovních plateb k fakturám.

Kontaktujte nás

Stav k 2. listopadu 2026. Verze modulu 19.0.1.0.0. Ukázkové údaje na obrázcích jsou smyšlené, částky jsou bez DPH.