Qui ven paquets amb Odoo, com ara un aparell de mesura amb accessoris i muntatge, un lloc de treball amb maquinari i configuració o un paquet de manteniment amb diversos serveis, necessita dues coses alhora en el pressupost: la composició ha de constar a la comanda perquè els equips de magatzem, tècnic i de facturació sàpiguen què es lliura. Ara bé, a la còpia impresa per al client no sempre hi han d’aparèixer totes les línies amb els preus individuals.
El mòdul de l’OCA sale_product_set permet afegir conjunts de productes (llistes de materials per a la venda) a un pressupost amb un sol clic. Però no determina com s’imprimeixen. Per això hem desenvolupat el mòdul Sale Product Set Layout (sale_product_set_layout). Està escrit per a Odoo 19 i està disponible gratuïtament a GitHub com a programari lliure amb la llicència AGPL-3:
github.com/Alex0176/odoo-sale-product-set-layout
Contingut d’aquest article
- El problema: conjunts de productes sense control sobre la presentació impresa
- Què fa el mòdul
- Els tres modes d’impressió en detall
- Línies ocultes i preu fix
- Implementació tècnica
- Instal·lació sense efectes secundaris no desitjats
- Ús pas a pas
- Proves
- Limitacions i avís legal
- Preguntes freqüents
- Suport de Biricon IT Services
El problema: conjunts de productes sense control sobre la presentació impresa
A Odoo, un conjunt de productes és una llista predefinida de productes amb les quantitats corresponents. En lloc de cercar deu articles un per un, seleccioneu Afegeix un conjunt al pressupost i hi obteniu totes les línies de cop. Això estalvia temps i evita que us deixeu cap article. El mòdul sale_product_set del repositori OCA/sale-workflow fa exactament això; els conjunts mateixos provenen de product_set, al repositori OCA/product-attribute.
A la pràctica, queden tres mancances:
- No hi ha una agrupació clara. Les línies del conjunt s’afegeixen al pressupost com a línies independents. Un cop inserides, ja no es veu quines formen un conjunt, i l’import total del conjunt no apareix enlloc.
- No es pot controlar la impressió. De vegades el client ha de veure cada línia amb el seu preu; d’altres, només el preu del paquet, i d’altres, una única línia. Aquesta decisió s’ha de poder prendre per a cada conjunt o client, sense haver de tornar-la a aplicar manualment a cada pressupost.
- Els ajustos de preu queden poc elegants. Si un paquet ha de tenir un preu fix diferent de la suma dels preus de tarifa, cal una línia d’ajust. Aquesta línia ha d’intervenir en el càlcul, però no necessàriament ha d’aparèixer al document que rep el client.
Odoo 19 ja incorpora els elements necessaris per resoldre-ho adequadament: seccions i subseccions a les línies del pressupost, amb les opcions Amaga els preus i Amaga la composició (camps collapse_prices i collapse_composition). El mòdul vincula el conjunt de productes a aquestes opcions estàndard, en lloc de crear un format d’impressió propi.
Què fa el mòdul
- Insereix el conjunt com a secció. L’assistent Afegeix un conjunt crea una línia de secció amb el nom del conjunt, per exemple 2 × Kit de mesura estàndard, i hi col·loca les línies del conjunt a sota. Les seccions internes del conjunt es converteixen en subseccions.
- Mode d’impressió per conjunt. Es defineix una vegada al conjunt com s’ha d’imprimir. En inserir-lo, es pot canviar el mode a l’assistent per a aquell cas concret.
- Amaga línies. Es poden marcar línies individuals com a No imprimeixis (al conjunt: Oculta). Es tenen en compte en l’import total, però no s’imprimeixen ni es mostren al portal del client. La marca es trasllada del pressupost a la factura.
- Preu fix per línia del conjunt. Una línia del conjunt pot tenir un preu unitari fix que substitueixi el de la tarifa, per exemple per a un ajust de preu ocult.
Sense el mòdul (només sale_product_set) | Amb sale_product_set_layout |
|---|---|
| Línies del conjunt com a línies independents | Línia de secció amb el nom del conjunt, quantitat inclosa al nom i línies a sota |
| Les seccions del conjunt es mantenen com a seccions i trenquen l’estructura | Les seccions del conjunt es converteixen en subseccions (line_subsection) |
| Presentació impresa sempre igual | Mode d’impressió per conjunt, modificable en inserir-lo |
| Ajust de preu visible al pressupost i a la factura | La línia es pot ocultar, però es té en compte en l’import total |
| Preu obtingut únicament de la tarifa | Preu fix opcional per línia del conjunt |
| El format només s’aplica al pressupost | El format i l’ocultació de línies s’apliquen al pressupost, la comanda, la factura i el portal del client |
Els tres modes d’impressió en detall
El mode d’impressió és un camp de selecció print_mode del conjunt de productes. En inserir el conjunt, es tradueix en les dues opcions estàndard de la línia de secció. Per fer-ho, el mòdul utilitza una única correspondència, fàcil de llegir:
# Mode d'impressió -> (collapse_prices, collapse_composition) de la línia de secció
PRINT_MODE_FLAGS = {
"detailed": (False, False),
"total_only": (True, False),
"summary": (False, True),
}
Mode (print_mode) | Visualització al conjunt | Opció estàndard d'Odoo | Presentació impresa |
|---|---|---|---|
detailed | Cada línia amb el preu | cap | Secció amb totes les línies i els imports |
total_only | Línies sense preus, només el total del conjunt | collapse_prices (Amaga els preus) | Secció amb el total i línies només amb la descripció i la quantitat |
summary | Una sola línia amb el total del conjunt | collapse_composition (Amaga la composició) | Només la línia de secció amb el total |
Com que el mòdul utilitza les opcions estàndard d'Odoo 19 i no un format d'impressió propi, el pressupost, la confirmació de comanda, la factura i el portal del client funcionen sense haver d'adaptar les plantilles dels informes. Les plantilles d'informe pròpies que tinguin en compte les opcions de secció també ofereixen aquest comportament.
Línies ocultes i preu fix
Un cas habitual: un paquet té un preu inferior a la suma dels preus individuals. En lloc d'alterar els preus individuals, es fa servir una línia d'ajust amb preu fix (per exemple, −50,00 € per conjunt) i s'amaga. A la interfície de gestió es pot comprovar el càlcul; al document imprès hi apareix el preu del paquet.
Cal tenir present que l'indicador hide_in_report no modifica el càlcul. La quantitat, el preu, el descompte, l'impost, els totals i els assentaments comptables es generen amb el funcionament estàndard d'Odoo, sense canvis. La línia només s'omet al document imprès i al portal.
- Al conjunt de productes: línia del conjunt amb Oculta i, opcionalment, Preu fix amb el Preu unitari corresponent.
- A les línies del pressupost i de la factura: columna No imprimeixis (
hide_in_report), també disponible per a línies normals que no pertanyen a cap conjunt. - Propagació: en crear la factura a partir de la comanda, l'indicador es transfereix a la línia de factura. En copiar un pressupost, es conserva.
Implementació tècnica
Hem mantingut el mòdul petit expressament: amplia els models existents i només sobreescriu els punts en què Odoo decideix quines línies s'imprimeixen i com s'insereix un conjunt. No incorpora plantilles d'informe noves, models nous ni modificacions mitjançant SQL.
Models i camps ampliats
| Model | Ampliació |
|---|---|
product.set | Camps insert_as_section (booleà, activat per defecte) i print_mode (selecció, obligatori, valor per defecte detailed) |
product.set.line | Camps hide_in_report, fixed_price i price_unit; prepare_sale_order_line_values() els transfereix a la línia de pressupost |
sale.product.set.wizard | Camps insert_as_section, section_name i print_mode; _prepare_order_lines() crea la línia de secció |
sale.order / sale.order.line | Camp hide_in_report (amb copy=True); _get_order_lines_to_report() omet les línies ocultes; _prepare_invoice_line() transfereix l'indicador |
account.move / account.move.line | Camp hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() i _get_subsection_lines() filtren les línies ocultes; _get_aml_vals() incorpora l'indicador a les dades de l'informe |
Ocultar línies al pressupost
Odoo 19 determina les línies de pressupost que s'han d'imprimir a _get_order_lines_to_report(). El mòdul hi afegeix un filtre. No cal modificar la plantilla de l'informe: tant la plantilla de l'informe del pressupost (sale/report/ir_actions_report_templates.xml) com la del portal (sale/views/sale_portal_templates.xml) criden aquest mètode:
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
)
Transferència a la factura
En generar la línia de factura a partir de la línia de pressupost (_prepare_invoice_line()), es copia l'indicador. A les factures d'Odoo 19, les línies poden arribar a l'informe per diverses vies: la llista principal, les línies d'una secció i les línies d'una subsecció. Per això el mòdul filtra en diversos punts (_get_move_lines_to_report() al document, _get_detailed_lines() i _get_subsection_lines() a la línia); altrament, una línia oculta podria tornar a aparèixer per alguna de les altres vies:
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")]
L'assistent: una secció en lloc de línies soltes
L'assistent sale.product.set.wizard de sale_product_set genera les línies de pressupost a _prepare_order_lines(). El mòdul sobreescriu aquest mètode. Si Insereix com a secció està desactivat, simplement crida el mètode original i manté el comportament del mòdul de l'OCA. En cas contrari, primer crea la línia de secció amb les dues opcions estàndard i, a continuació, les línies del conjunt. Les seccions internes del conjunt es converteixen en subseccions perquè no trenquin l'estructura 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
Decisions que no es veuen
- Mètodes de càlcul separats a l'assistent. El nom de la secció, el mode d'impressió i Insereix com a secció tenen cadascun un mètode
computepropi (store=True,readonly=False). Si tots tres compartissin el mateix mètode, un valor assignat explícitament a un camp restabliria els altres. Això es nota sobretot quan es crea l'assistent amb valors des del codi o des de les proves. - Nom de secció amb la quantitat. Si la quantitat és diferent d'1, s'inclou al nom (2 × Kit de mesura estàndard, amb coma com a separador decimal), perquè al document imprès es vegi que són dos conjunts i no un conjunt amb el doble de quantitat.
- Preu fix només a les línies de producte.
prepare_sale_order_line_values()només estableixhide_in_reportiprice_unitsi la línia del conjunt no és una línia de secció ni de nota. - Funcions estàndard d'Odoo en lloc d'una solució pròpia. El mode d'impressió i l'ocultació utilitzen els punts d'extensió i els camps existents d'Odoo 19. Això facilita les actualitzacions: si Odoo canvia la plantilla de l'informe, el mòdul continua funcionant mentre es mantinguin els mètodes.
Instal·lació sense efectes secundaris no desitjats
Requisits: Odoo 19.0, sale_product_set (OCA/sale-workflow, branca 19.0) i product_set (OCA/product-attribute, branca 19.0). El mòdul mateix depèn de sale_management, sale_product_set i account.
El parany d'auto_install als mòduls de l'OCA. Molts repositoris de l'OCA contenen mòduls amb auto_install. Quan les seves dependències estan disponibles, Odoo també els instal·la sense preguntar durant la següent instal·lació o actualització. Per tant, si s'afegeix tot el repositori de l'OCA a l'addons_path d'una instància de producció, és possible que s'instal·lin mòduls que ningú no volia. A OCA/product-attribute, això afecta, per exemple, purchase_product_template_tags. Nosaltres seguim aquests passos:
- Fer una clonació superficial dels dos repositoris de l'OCA (
git clone --depth 1 --branch 19.0). - Crear en un directori propi enllaços simbòlics només als dos mòduls necessaris i afegir aquest directori a l'
addons_path, en lloc dels repositoris sencers. - Instal·lar en una base de dades de proves i comprovar quins mòduls passen a l'estat instal·lat.
- Només llavors, instal·lar a producció amb
--workers=0, perquè l'ordre no competeixi pel port HTTP amb el servei en execució.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init
Després de la instal·lació, els camps i les vistes nous ja són a la base de dades. Si el servei funciona amb diversos processos, els processos de treball carreguen el registre de models modificat mitjançant el senyal de la base de dades. Els canvis als fitxers Python del mòdul només tenen efecte després de reiniciar el servei.
Ús pas a pas
- Creeu un conjunt: Vendes → Productes → Conjunts de productes. Poseu un nom al conjunt, seleccioneu Insereix com a secció i el Mode d'impressió, i introduïu els productes amb les quantitats corresponents. Marqueu les línies d'ajust amb Oculta i Preu fix.
- Al pressupost: feu clic a Afegeix un conjunt i seleccioneu el conjunt i la quantitat. Podeu canviar el nom de la secció i el mode d'impressió abans d'inserir-lo.
- Oculteu línies individuals: marqueu la casella de la columna No imprimeixis al pressupost o a la factura.
- Comproveu el resultat: obriu la previsualització o el PDF. Els clients veuen la mateixa presentació al portal.
Proves
El mòdul inclou sis proves automatitzades que executem en una base de dades de proves abans de passar res a producció:
- Inserció com a secció, amb mode d'impressió i subsecció
- La línia oculta no apareix entre les línies que s'han d'imprimir
- Mode Una línia (
summary) - Inserció sense secció, amb el comportament original
- La factura hereta l'indicador
hide_in_report, la línia no apareix a_get_move_lines_to_report()i el total de la secció la continua incloent - La factura en mode Només el preu total no mostra la línia oculta entre les línies de la secció i manté íntegre el total de la secció
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
--test-enable --test-tags=/sale_product_set_layout --stop-after-init
La traducció a l'alemany és a i18n/de.po.
Limitacions i avís legal
- Només la presentació. El mòdul no modifica la manera com Odoo calcula els preus, els descomptes o els impostos. Només canvia què s'imprimeix i què es mostra al portal.
- Les línies ocultes són visibles a la interfície de gestió. L'indicador afecta el document imprès i el portal, però no la vista en pantalla dels empleats.
- Comproveu les dades de les factures. Les línies ocultes es comptabilitzen en el total, però no apareixen al document. Abans d'utilitzar aquesta funció a les factures, consulteu amb la vostra assessoria fiscal si és compatible amb els requisits de facturació del vostre país, com ara la descripció obligatòria dels béns lliurats o dels serveis prestats. Ocultar línies als pressupostos planteja menys problemes. Aquest article no constitueix assessorament jurídic.
- Provat amb Odoo 19. Hem desenvolupat i provat el mòdul per a Odoo 19.0 Community. Utilitza opcions de secció disponibles amb aquesta forma a Odoo 19. No l'hem provat amb altres versions.
Preguntes freqüents
Per què necessito el mòdul si Odoo 19 ja permet crear seccions?
Odoo ja incorpora les opcions de secció. El que faltava era la connexió amb el conjunt de productes: el conjunt s'havia d'inserir automàticament com a secció, amb el mode d'impressió definit al conjunt. Sense el mòdul, cal crear i configurar manualment la línia de secció després de cada inserció.
Quina diferència hi ha entre «Amaga els preus» i «Amaga la composició»?
«Amaga els preus» (collapse_prices) manté les línies, però no en mostra els imports, només el total de la secció. «Amaga la composició» (collapse_composition) oculta les línies del tot: el client només veu la secció amb el total.
Una línia oculta es comptabilitza en el total de la factura?
Sí. Les línies ocultes es continuen incloent en el total, els impostos i la comptabilització. L'indicador només afecta la presentació al document imprès i al portal del client.
Puc canviar el mode d'impressió en inserir el conjunt?
Sí. El mode definit al conjunt es fa servir com a valor per defecte, però es pot canviar a l'assistent per a cada inserció. El conjunt mateix no es modifica.
Què passa si desactivo Insereix com a secció?
L'assistent es comporta com al mòdul de l'OCA sale_product_set: les línies del conjunt s'insereixen sense secció.
El mòdul instal·la altres mòduls inesperadament?
El mòdul mateix, no. Ara bé, si s'afegeixen repositoris sencers de l'OCA a l'addons_path, es poden instal·lar mòduls amb auto_install. Per això, creeu enllaços només als dos mòduls necessaris (vegeu l'apartat d'instal·lació).
El mòdul funciona amb Odoo Enterprise?
El mòdul només depèn de mòduls de Community (sale_management, account) i dels dos mòduls de l'OCA. Nosaltres el fem servir amb Odoo 19 Community. No l'hem provat amb Enterprise.
Quina llicència té el mòdul?
La llicència AGPL-3. El podeu utilitzar, adaptar i redistribuir sempre que les modificacions mantinguin la mateixa llicència. Els informes d'errors i els suggeriments de millora en forma d'incidència al repositori de GitHub són benvinguts.
Suport de Biricon IT Services
Ens ocupem d'instal·lacions d'Odoo per a petites i mitjanes empreses: des de la implantació fins al funcionament diari, passant per les adaptacions i les migracions. Si voleu adaptar els conjunts de productes, les plantilles de pressupost o els informes d'Odoo 19 als vostres processos, o si voleu avaluar un mòdul com aquest i incorporar-lo amb garanties a la vostra instància de producció, us podem ajudar. Trobareu altres desenvolupaments propis al nostre blog, com ara Fusiona factures en esborrany i Assigna automàticament els pagaments bancaris a les factures.
Poseu-vos en contacte amb nosaltres
Actualitzat el 2 de novembre de 2026. Versió del mòdul: 19.0.1.0.0. Les dades d'exemple de les imatges són fictícies; els imports no inclouen impostos.