Sari la conținut

Odoo 19 Community: seturi de produse în ofertă ca secțiune – modulul nostru open source Sale Product Set Layout

2 noiembrie 2026 | Odoo 19 Community | Dezvoltare proprie
2 noiembrie 2026 de
Odoo 19 Community: seturi de produse în ofertă ca secțiune – modulul nostru open source Sale Product Set Layout

Modulul Odoo 19 Sale Product Set Layout: seturi de produse ca secțiune în ofertă, cu mod de tipărire, linii ascunse și preț fix

Cine vinde pachete în Odoo, de exemplu un aparat de măsură cu accesorii și montaj, un loc de muncă cu hardware și configurare sau un pachet de mentenanță format din mai multe servicii, are nevoie în ofertă de două lucruri în același timp: componența trebuie să figureze în comandă, pentru ca depozitul, echipa tehnică și facturarea să știe ce se livrează. Pe documentul tipărit pentru client, în schimb, nu trebuie să apară în orice caz fiecare poziție cu fiecare preț unitar.

Modulul OCA sale_product_set permite inserarea seturilor de produse (liste de materiale pentru vânzare) într-o ofertă cu un singur clic. Nu decide însă aspectul tipăritului. Pentru aceasta am dezvoltat modulul Sale Product Set Layout (sale_product_set_layout). Este scris pentru Odoo 19 și este disponibil gratuit pe GitHub ca software liber sub licența AGPL-3:

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

Problema: seturile de produse nu influențează aspectul tipăritului

Un set de produse în Odoo este o listă pregătită de produse cu cantități. În loc să caute zece articole pe rând, utilizatorul alege în ofertă Adăugare set și primește toate liniile deodată. Aceasta economisește timp și previne omiterea unor poziții. Modulul sale_product_set din repository-ul OCA/sale-workflow face exact acest lucru; seturile în sine provin din product_set din repository-ul OCA/product-attribute.

În practică rămân trei lacune:

  • Nicio grupare vizibilă. Liniile setului ajung în ofertă ca poziții separate. După inserare nu se mai vede care linii aparțin împreună, iar totalul setului nu apare nicăieri.
  • Nicio influență asupra tipăritului. Uneori clientul trebuie să vadă fiecare poziție cu preț, alteori doar prețul pachetului, alteori doar o singură linie. Aceasta este o decizie per set sau per client, nu de luat manual de fiecare dată pentru fiecare ofertă.
  • Corecțiile de preț sunt neplăcute. Dacă un pachet trebuie să aibă un preț fix, care diferă de suma prețurilor de listă, este nevoie de o linie de corecție. Ea aparține calculului, dar nu neapărat hârtiei clientului.

Odoo 19 oferă elementele necesare pentru a rezolva curat problema: secțiuni și subsecțiuni în liniile ofertei, cu opțiunile Ascundere prețuri și Ascundere componență (câmpurile collapse_prices și collapse_composition). Modulul conectează setul de produse la aceste opțiuni standard, în loc să inventeze un format de tipărire propriu.

Ce face modulul

  1. Inserarea setului ca secțiune. Asistentul Adăugare set creează o linie de secțiune cu numele setului, de exemplu 2 × Messkit Standard, și plasează dedesubt liniile setului. Secțiunile din interiorul setului devin subsecțiuni.
  2. Mod de tipărire pe set. În set se stabilește o singură dată cum este tipărit. La inserare, modul poate fi schimbat în asistent pentru cazul respectiv.
  3. Ascunderea liniilor. Liniile individuale pot fi marcate ca Nu se tipărește (în set: Ascuns). Ele se includ în sumă, dar nu sunt nici tipărite, nici afișate în portalul clientului. Indicatorul se transferă de la ofertă la factură.
  4. Preț fix pe linie de set. O linie de set poate avea un preț unitar fix, care înlocuiește prețul din lista de prețuri, de exemplu pentru o corecție de preț ascunsă.
Fără modul (doar sale_product_set)Cu sale_product_set_layout
Liniile setului ca poziții separateLinie de secțiune cu numele setului, cantitatea în nume, liniile dedesubt
Secțiunile din set rămân secțiuni și rup structuraSecțiunile din set devin subsecțiuni (line_subsection)
Aspect tipărit mereu identicMod de tipărire pe set, modificabil la inserare
Corecția de preț vizibilă în ofertă și în facturăLinie ascunsă, dar inclusă în sumă
Preț doar din lista de prețuriOpțional preț fix pe linie de set
Aspectul se oprește la ofertăAspectul și ascunderea se aplică ofertei, comenzii, facturii și portalului clientului

Cele trei moduri de tipărire în detaliu

Modul de tipărire este un câmp de selecție print_mode la setul de produse. La inserare este tradus în cele două comutatoare standard ale liniei de secțiune. Pentru aceasta, modulul conține o singură corespondență, ușor de citit:

# Mod de tipărire -> (collapse_prices, collapse_composition) ale liniei de secțiune
PRINT_MODE_FLAGS = {
    "detailed":   (False, False),
    "total_only": (True,  False),
    "summary":    (False, True),
}

Trei aspecte tipărite ale aceluiași set de produse: fiecare linie cu preț, doar prețul total, o singură linie

Mod (print_mode)Afișare în setOpțiune standard în OdooAspect tipărit
detailedFiecare linie cu prețniciunaSecțiune, toate liniile cu sumă
total_onlyLinii fără prețuri, doar suma setuluicollapse_prices (Ascundere prețuri)Secțiune cu sumă, liniile doar cu denumire și cantitate
summaryDoar o linie cu suma setuluicollapse_composition (Ascundere componență)Doar linia de secțiune cu suma

Deoarece modulul folosește opțiunile standard din Odoo 19 și nu un format de tipărire propriu, oferta, confirmarea comenzii, factura și portalul clientului funcționează fără șabloane de raport personalizate. Cine are șabloane de raport proprii care țin cont de opțiunile secțiunilor obține același comportament.

Linii ascunse și preț fix

Un caz frecvent: un pachet are un preț de pachet mai mic decât suma prețurilor individuale. În loc să modifice prețurile individuale, se adaugă o linie de corecție cu preț fix (de exemplu −50,00 € pe set) și se ascunde. În backend, factura este ușor de urmărit, iar pe tipărit apare prețul pachetului.

Corecția de preț vizibilă și marcată în backend, nevizibilă în tipărirea ofertei, PDF, factură și portal, suma identică

Important pentru înțelegere: indicatorul hide_in_report nu schimbă nimic în calcul. Cantitatea, prețul, discountul, taxa, sumele și înregistrările contabile rezultă neschimbate din Odoo standard. Linia lipsește doar din tipărit și din portal.

  • La setul de produse: linie de set cu Ascuns și, opțional, Preț fix împreună cu Preț unitar.
  • La liniile ofertei și ale facturii: coloana Nu se tipărește (hide_in_report), inclusiv pentru liniile normale, fără set.
  • Transmitere: la crearea facturii din comandă, indicatorul este transferat pe linia facturii. La copierea unei oferte, el se păstrează.

Implementare tehnică

Modulul este intenționat mic: extinde modelele existente și suprascrie doar locurile în care Odoo decide ce linii se tipăresc și cum se inserează un set. Nu există șabloane de raport noi, modele noi sau intervenții SQL.

Fluxul de date: set de produse, asistentul Adăugare set, linii de ofertă, linii de factură, PDF și portalul clientului

Modele și câmpuri extinse

ModelCompletare
product.setCâmpurile insert_as_section (boolean, implicit activat) și print_mode (selecție, câmp obligatoriu, implicit detailed)
product.set.lineCâmpurile hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() le transmite liniei ofertei
sale.product.set.wizardCâmpurile insert_as_section, section_name, print_mode; _prepare_order_lines() construiește linia de secțiune
sale.order / sale.order.lineCâmpul hide_in_report (cu copy=True); _get_order_lines_to_report() omite liniile ascunse; _prepare_invoice_line() transmite indicatorul
account.move / account.move.lineCâmpul hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() și _get_subsection_lines() filtrează liniile ascunse; _get_aml_vals() trece indicatorul în datele raportului

Ascunderea în ofertă

Odoo 19 stabilește liniile de ofertă care trebuie tipărite în _get_order_lines_to_report(). Modulul adaugă acolo un filtru. O intervenție în șablonul de raport nu este necesară: atât șablonul de raport al ofertei (sale/report/ir_actions_report_templates.xml), cât și șablonul portalului (sale/views/sale_portal_templates.xml) apelează această metodă:

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
        )

Transmiterea către factură

La generarea liniei de factură din linia ofertei (_prepare_invoice_line()), indicatorul este copiat. Pe partea facturii, în Odoo 19 există mai multe căi prin care liniile ajung în raport: lista principală, liniile unei secțiuni și liniile unei subsecțiuni. De aceea modulul filtrează în mai multe locuri (_get_move_lines_to_report() la nivel de document, _get_detailed_lines() și _get_subsection_lines() la nivel de linie), altfel o linie ascunsă ar putea reapărea pe una dintre celelalte căi:

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

Asistentul: secțiune în loc de linii separate

Asistentul sale.product.set.wizard din sale_product_set generează liniile ofertei în _prepare_order_lines(). Modulul suprascrie această metodă. Dacă Inserare ca secțiune este dezactivat, apelează pur și simplu metoda originală, iar comportamentul rămâne ca în modulul OCA. În caz contrar, se creează mai întâi linia de secțiune cu cele două comutatoare standard, apoi urmează liniile setului. Secțiunile din interiorul setului devin subsecțiuni, pentru a nu rupe structura principală:

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

Decizii care nu se văd

  • Metode de calcul separate în asistent. Numele secțiunii, modul de tipărire și Inserare ca secțiune au fiecare propria metodă compute (store=True, readonly=False). Dacă toate trei ar avea aceeași metodă, o valoare transmisă explicit pentru un câmp ar reseta celelalte. Acest lucru se observă mai ales când asistentul este creat din cod sau din teste, cu valori predefinite.
  • Numele secțiunii cu cantitatea. Dacă cantitatea este diferită de 1, ea apare în nume (2 × Messkit Standard, cu virgulă ca separator zecimal), pentru ca pe tipărit să se vadă că sunt două seturi și nu un set cu cantitate dublă.
  • Preț fix doar la liniile de produs. prepare_sale_order_line_values() setează hide_in_report și price_unit numai dacă linia setului nu este o linie de secțiune sau de notă.
  • Odoo standard în loc de soluție proprie. Modul de tipărire și ascunderea folosesc hook-uri și câmpuri existente din Odoo 19. Aceasta face actualizările gestionabile: dacă Odoo modifică șablonul de raport, modulul rămâne valabil atât timp cât metodele există.

Instalare fără efecte secundare nedorite

Cerințe: Odoo 19.0, sale_product_set (OCA/sale-workflow, branch 19.0) și product_set (OCA/product-attribute, branch 19.0). Modulul în sine depinde de sale_management, sale_product_set și account.

Capcana auto_install la modulele OCA. Multe repository-uri OCA conțin module cu auto_install. De îndată ce dependențele lor sunt prezente, Odoo le instalează nechemat la următoarea instalare sau actualizare. Cine pune deci întregul repository OCA în addons_path al unei instanțe de producție riscă să primească module pe care nimeni nu le-a dorit. La OCA/product-attribute acest lucru se aplică, de exemplu, la purchase_product_template_tags. Procedura noastră:

  1. Clonați superficial cele două repository-uri OCA (git clone --depth 1 --branch 19.0).
  2. Într-un director separat, legați prin symlink doar cele două module necesare și adăugați acest director în addons_path, nu repository-urile întregi.
  3. Instalați pe o bază de date de test și verificați ce module primesc în acest timp starea instalat.
  4. Abia apoi instalați în producție, cu --workers=0, pentru ca apelul să nu intre în conflict cu serviciul care rulează pentru portul HTTP.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init

Câmpurile și vizualizările noi se află în baza de date după instalare. Dacă serviciul rulează în mod multiproces, worker-ii reîncarcă registry-ul modificat prin semnalul bazei de date. Modificările în fișierele Python ale modulului devin active abia după o repornire a serviciului.

Utilizare pas cu pas

  1. Crearea unui set: Vânzări → Produse → Seturi de produse. Denumiți setul, alegeți Inserare ca secțiune și Mod de tipărire, introduceți produsele cu cantități. Marcați liniile de corecție cu Ascuns și Preț fix.
  2. În ofertă: faceți clic pe Adăugare set, alegeți setul și cantitatea. Numele secțiunii și modul de tipărire pot fi modificate înainte de inserare.
  3. Ascunderea unor linii individuale: în ofertă sau în factură, activați coloana Nu se tipărește.
  4. Verificare: deschideți previzualizarea sau PDF-ul. Aceeași afișare o văd clienții în portal.

Teste

Modulul vine cu șase teste automate, pe care le rulăm pe o bază de date de test înainte ca ceva să intre în producție:

  • Inserare ca secțiune, inclusiv mod de tipărire și subsecțiune
  • Linia ascunsă lipsește din liniile de tipărit
  • Modul O linie (summary)
  • Inserare fără secțiune, comportamentul inițial
  • Factura preia indicatorul hide_in_report, linia lipsește din _get_move_lines_to_report(), suma secțiunii o include în continuare
  • Factura în modul Doar prețul total nu afișează linia ascunsă în liniile secțiunii, suma secțiunii rămâne completă
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
         --test-enable --test-tags=/sale_product_set_layout --stop-after-init

Traducerea în limba germană se află în i18n/de.po.

Limite și notă juridică

  • Doar aspect. Modulul nu modifică modul în care Odoo calculează prețurile, discounturile sau taxele. Modifică doar ce se tipărește și ce se afișează în portal.
  • Liniile ascunse sunt vizibile în backend. Indicatorul privește tipăritul și portalul, nu vizualizarea pe ecran a angajaților.
  • Verificați mențiunile din factură. Liniile ascunse se includ în sumă, dar nu apar pe document. Dacă acest lucru este compatibil cu cerințele privind facturile din țara dumneavoastră, de exemplu cu descrierea obligatorie a bunurilor livrate sau a serviciilor prestate, trebuie să clarificați înainte de utilizarea pe facturi cu consultantul dumneavoastră fiscal. Pe oferte, ascunderea este mai puțin problematică. Articolul nu reprezintă consultanță juridică.
  • Testat pentru Odoo 19. Am dezvoltat și testat modulul pentru Odoo 19.0 Community. Folosește opțiuni ale secțiunilor care există în această formă în Odoo 19. Pentru alte versiuni nu l-am verificat.

Întrebări frecvente

De ce am nevoie de modul, dacă Odoo 19 știe deja secțiuni?

Opțiunile secțiunilor există în Odoo însuși. Lipsea legătura cu setul de produse: un set trebuia inserat automat ca secțiune, cu modul de tipărire stabilit în set. Fără modul, după fiecare inserare trebuie creată și configurată manual linia de secțiune.

Prin ce diferă „Ascundere prețuri” de „Ascundere componență”?

„Ascundere prețuri” (collapse_prices) lasă liniile, dar nu afișează sumele, ci doar totalul la nivelul secțiunii. „Ascundere componență” (collapse_composition) ascunde complet liniile, iar clientul vede doar secțiunea cu totalul.

Se include o linie ascunsă în totalul facturii?

Da. Liniile ascunse rămân în total, în taxă și în înregistrarea contabilă. Indicatorul privește doar afișarea pe tipărit și în portalul clientului.

Pot modifica modul de tipărire la inserare?

Da. Modul vine ca valoare implicită din set, iar în asistent poate fi modificat pentru inserarea respectivă. Setul în sine rămâne neschimbat.

Ce se întâmplă dacă dezactivez Inserare ca secțiune?

Atunci asistentul se comportă ca în modulul OCA sale_product_set: liniile setului sunt inserate fără secțiune.

Instalează modulul neașteptat alte module?

Modulul în sine nu. La repository-urile OCA pot apărea însă module auto_install, dacă se pun repository-uri întregi în addons_path. De aceea, legați prin symlink doar cele două module necesare (vezi secțiunea Instalare).

Rulează modulul cu Odoo Enterprise?

Modulul depinde doar de module Community (sale_management, account) și de cele două module OCA. Îl folosim pe Odoo 19 Community. Pe Enterprise nu l-am testat.

Sub ce licență este modulul?

Sub AGPL-3. Îl puteți folosi, adapta și distribui, cu condiția ca modificările să rămână sub aceeași licență. Erorile și propunerile de îmbunătățire sunt binevenite ca issue în repository-ul GitHub .

Asistență din partea Biricon IT Services

Administrăm instalări Odoo pentru întreprinderi mici și mijlocii: de la implementare, prin personalizări și migrări, până la funcționarea curentă. Dacă doriți să adaptați seturile de produse, șabloanele de ofertă sau rapoartele din Odoo 19 la procesele dumneavoastră, sau dacă doriți să verificați un modul ca acesta și să-l puneți în siguranță în instanța dumneavoastră de producție, vă sprijinim cu plăcere. Alte dezvoltări proprii găsiți în blogul nostru, de exemplu Îmbinarea facturilor în ciornă și Alocarea automată a plăților bancare la facturi.

Contactați-ne acum

Situație la: 2 noiembrie 2026. Versiunea modulului 19.0.1.0.0. Datele de exemplu din figuri sunt alese liber, sume nete.