Chi vende pacchetti in Odoo, ad esempio uno strumento di misura con accessori e montaggio, una postazione di lavoro con hardware e configurazione o un pacchetto di manutenzione composto da più prestazioni, ha bisogno nel preventivo di due cose contemporaneamente: la composizione deve figurare nell’ordine, affinché magazzino, tecnici e fatturazione sappiano che cosa viene consegnato. Sulla stampa per il cliente, però, non sempre ogni singola posizione deve comparire con il proprio prezzo.
Il modulo OCA sale_product_set consente di inserire con un clic nel preventivo i set di prodotti (distinte base per la vendita). Non decide però nulla sull’aspetto della stampa. Per questo abbiamo sviluppato il modulo Sale Product Set Layout (sale_product_set_layout). È scritto per Odoo 19 ed è disponibile gratuitamente su GitHub come software libero con licenza AGPL-3:
github.com/Alex0176/odoo-sale-product-set-layout
Contenuto di questo articolo
- Il problema: set di prodotti senza alcuna influenza sulla stampa
- Cosa fa il modulo
- Le tre modalità di stampa in dettaglio
- Righe nascoste e prezzo fisso
- Implementazione tecnica
- Installazione senza effetti collaterali indesiderati
- Utilizzo passo per passo
- Test
- Limiti e avvertenza legale
- Domande frequenti
- Supporto da parte di Biricon IT Services
Il problema: set di prodotti senza alcuna influenza sulla stampa
In Odoo un set di prodotti è un elenco predefinito di prodotti con le relative quantità. Invece di cercare dieci articoli uno per uno, nel preventivo si sceglie Aggiungi set e si ottengono tutte le righe in un colpo solo. Ciò fa risparmiare tempo e impedisce di dimenticare posizioni. Il modulo sale_product_set del repository OCA/sale-workflow fa esattamente questo; i set stessi provengono da product_set nel repository OCA/product-attribute.
Nella pratica restano tre lacune:
- Nessun raggruppamento riconoscibile. Le righe del set finiscono nel preventivo come posizioni sciolte. Dopo l’inserimento non si vede più quali righe appartengano insieme e il totale del set non compare da nessuna parte.
- Nessuna influenza sulla stampa. A volte il cliente deve vedere ogni posizione con il prezzo, a volte solo il prezzo del pacchetto, a volte una sola riga. Si tratta di una decisione per ogni set o per ogni cliente, non da rifare a mano per ogni preventivo.
- Le correzioni di prezzo sono poco eleganti. Se un pacchetto deve avere un prezzo fisso che si discosta dalla somma dei prezzi di listino, serve una riga di correzione. Questa appartiene al calcolo, ma non necessariamente alla carta del cliente.
Odoo 19 offre gli elementi per risolvere il problema in modo pulito: sezioni e sottosezioni nelle righe del preventivo con le opzioni Nascondi prezzi e Nascondi composizione (campi collapse_prices e collapse_composition). Il modulo collega il set di prodotti a queste opzioni standard, invece di inventare un formato di stampa proprio.
Cosa fa il modulo
- Inserire il set come sezione. La procedura guidata Aggiungi set crea una riga di sezione con il nome del set, ad esempio 2 × Kit di misura Standard, e colloca sotto di essa le righe del set. Le sezioni all’interno del set diventano sottosezioni.
- Modalità di stampa per set. Nel set si stabilisce una volta per tutte come viene stampato. Al momento dell’inserimento la modalità può essere modificata nella procedura guidata per quel singolo caso.
- Nascondere righe. Singole righe possono essere contrassegnate come Non stampare (nel set: Nascosta). Contano nel totale, ma non vengono né stampate né mostrate nel portale clienti. Il contrassegno passa dal preventivo alla fattura.
- Prezzo fisso per riga del set. Una riga del set può avere un prezzo unitario fisso che sostituisce il prezzo del listino, ad esempio per una correzione di prezzo nascosta.
Senza il modulo (solo sale_product_set) | Con sale_product_set_layout |
|---|---|
| Righe del set come posizioni sciolte | Riga di sezione con il nome del set, quantità nel nome, righe sottostanti |
| Le sezioni nel set restano sezioni e spezzano la struttura | Le sezioni nel set diventano sottosezioni (line_subsection) |
| Aspetto di stampa sempre identico | Modalità di stampa per set, modificabile all’inserimento |
| Correzione di prezzo visibile su preventivo e fattura | Riga nascondibile, ma conteggiata nel totale |
| Prezzo solo dal listino | Prezzo fisso opzionale per riga del set |
| Il layout termina al preventivo | Layout e occultamento valgono per preventivo, ordine, fattura e portale clienti |
Le tre modalità di stampa in dettaglio
La modalità di stampa è un campo di selezione print_mode sul set di prodotti. All’inserimento viene tradotta nei due interruttori standard della riga di sezione. A tale scopo il modulo contiene un’unica associazione, facilmente leggibile:
# Druckmodus -> (collapse_prices, collapse_composition) der Abschnittszeile
PRINT_MODE_FLAGS = {
"detailed": (False, False),
"total_only": (True, False),
"summary": (False, True),
}
Modalità (print_mode) | Visualizzazione nel set | Opzione standard in Odoo | Aspetto di stampa |
|---|---|---|---|
detailed | Ogni riga con prezzo | nessuna | Sezione, tutte le righe con importo |
total_only | Righe senza prezzi, solo il totale del set | collapse_prices (Nascondi prezzi) | Sezione con totale, righe solo con descrizione e quantità |
summary | Una sola riga con il totale del set | collapse_composition (Nascondi composizione) | Solo la riga di sezione con il totale |
Poiché il modulo utilizza le opzioni standard di Odoo 19 e non un formato di stampa proprio, preventivo, conferma d’ordine, fattura e portale clienti funzionano senza modelli di report personalizzati. Chi dispone di modelli di report propri che tengono conto delle opzioni di sezione ottiene lo stesso comportamento.
Righe nascoste e prezzo fisso
Un caso frequente: un pacchetto ha un prezzo forfettario inferiore alla somma dei prezzi singoli. Invece di alterare i prezzi singoli, si usa una riga di correzione con prezzo fisso (ad esempio −50,00 € per set) e la si nasconde. Nel backend la fattura resta comprensibile, sulla stampa compare il prezzo del pacchetto.
Importante per la comprensione: il contrassegno hide_in_report non modifica nulla nel calcolo. Quantità, prezzo, sconto, imposta, totali e registrazioni contabili derivano invariati dall’Odoo standard. La riga manca soltanto nella stampa e nel portale.
- Sul set di prodotti: riga del set con Nascosta e, facoltativamente, Prezzo fisso con Prezzo unitario.
- Sulle righe di preventivo e fattura: colonna Non stampare (
hide_in_report), anche per le righe normali senza set. - Trasferimento: alla creazione della fattura dall’ordine il contrassegno viene riportato sulla riga di fattura. Copiando un preventivo, viene mantenuto.
Implementazione tecnica
Il modulo è volutamente piccolo: estende i modelli esistenti e sovrascrive solo i punti in cui Odoo decide quali righe vengono stampate e come viene inserito un set. Non ci sono nuovi modelli di report, nuovi modelli di dati né interventi SQL.
Modelli e campi estesi
| Modello | Aggiunta |
|---|---|
product.set | Campi insert_as_section (booleano, attivo per impostazione predefinita) e print_mode (selezione, obbligatorio, predefinito detailed) |
product.set.line | Campi hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() li trasmette alla riga del preventivo |
sale.product.set.wizard | Campi insert_as_section, section_name, print_mode; _prepare_order_lines() costruisce la riga di sezione |
sale.order / sale.order.line | Campo hide_in_report (con copy=True); _get_order_lines_to_report() salta le righe nascoste; _prepare_invoice_line() trasmette il contrassegno |
account.move / account.move.line | Campo hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() e _get_subsection_lines() filtrano le righe nascoste; _get_aml_vals() riporta il contrassegno nei dati del report |
Nascondere nel preventivo
Odoo 19 determina le righe di preventivo da stampare in _get_order_lines_to_report(). Il modulo vi aggiunge un filtro. Non è necessario alcun intervento sul modello di report: sia il modello di report del preventivo (sale/report/ir_actions_report_templates.xml) sia il modello del portale (sale/views/sale_portal_templates.xml) richiamano questo metodo:
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
)
Trasferimento alla fattura
Quando la riga di fattura viene generata dalla riga del preventivo (_prepare_invoice_line()), il contrassegno viene copiato. Sul lato fattura, in Odoo 19 le righe raggiungono il report attraverso più percorsi: l’elenco principale, le righe di una sezione e le righe di una sottosezione. Per questo il modulo filtra in più punti (_get_move_lines_to_report() sul documento, _get_detailed_lines() e _get_subsection_lines() sulla riga); altrimenti una riga nascosta potrebbe riapparire attraverso uno degli altri percorsi:
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")]
La procedura guidata: sezione invece di righe sciolte
La procedura guidata sale.product.set.wizard di sale_product_set genera le righe del preventivo in _prepare_order_lines(). Il modulo sovrascrive questo metodo. Se Inserisci come sezione è disattivato, richiama semplicemente il metodo originale e il comportamento resta quello del modulo OCA. Altrimenti viene creata prima la riga di sezione con i due interruttori standard, poi seguono le righe del set. Le sezioni all’interno del set diventano sottosezioni, in modo da non spezzare la struttura principale:
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
Decisioni che non si vedono
- Metodi di calcolo separati nella procedura guidata. Nome della sezione, modalità di stampa e Inserisci come sezione hanno ciascuno un proprio metodo
compute(store=True,readonly=False). Se tutti e tre usassero lo stesso metodo, un valore passato esplicitamente per un campo reimposterebbe gli altri. Ciò si nota soprattutto quando la procedura guidata viene creata con valori da codice o da test. - Nome della sezione con quantità. Se la quantità è diversa da 1, compare nel nome (2 × Kit di misura Standard, con la virgola come separatore decimale), così che nella stampa si riconosca che si tratta di due set e non di un set con quantità doppia.
- Prezzo fisso solo sulle righe prodotto.
prepare_sale_order_line_values()impostahide_in_reporteprice_unitsolo se la riga del set non è una riga di sezione o di nota. - Odoo standard invece di soluzioni proprie. Modalità di stampa e occultamento utilizzano hook e campi già presenti in Odoo 19. Ciò rende gli aggiornamenti gestibili: se Odoo modifica il modello di report, il modulo resta valido finché i metodi esistono.
Installazione senza effetti collaterali indesiderati
Requisiti: Odoo 19.0, sale_product_set (OCA/sale-workflow, branch 19.0) e product_set (OCA/product-attribute, branch 19.0). Il modulo stesso dipende da sale_management, sale_product_set e account.
La trappola auto_install nei moduli OCA. Molti repository OCA contengono moduli con auto_install. Non appena le loro dipendenze sono presenti, Odoo li installa senza chiedere alla successiva installazione o al successivo aggiornamento. Chi quindi inserisce l’intero repository OCA nell’addons_path di un’istanza di produzione rischia di ritrovarsi moduli che nessuno voleva. In OCA/product-attribute riguarda ad esempio purchase_product_template_tags. Il nostro modo di procedere:
- Clonare i due repository OCA in modalità shallow (
git clone --depth 1 --branch 19.0). - In una directory a parte collegare tramite symlink solo i due moduli necessari e aggiungere questa directory all’
addons_path, non gli interi repository. - Installare su un database di test e verificare quali moduli ottengono lo stato installato.
- Solo allora installare in produzione, con
--workers=0, affinché la chiamata non entri in conflitto con il servizio in esecuzione per la porta HTTP.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init
Dopo l’installazione, nuovi campi e viste sono presenti nel database. Se il servizio gira in modalità multiprocesso, i worker ricaricano il registry modificato tramite il segnale del database. Le modifiche ai file Python del modulo diventano efficaci solo dopo un riavvio del servizio.
Utilizzo passo per passo
- Creare un set: Vendite → Prodotti → Set di prodotti. Assegnare un nome al set, scegliere Inserisci come sezione e Modalità di stampa, inserire i prodotti con le quantità. Assegnare alle righe di correzione Nascosta e Prezzo fisso.
- Nel preventivo: fare clic su Aggiungi set, scegliere set e quantità. Nome della sezione e modalità di stampa possono essere modificati prima dell’inserimento.
- Nascondere singole righe: nel preventivo o nella fattura attivare la colonna Non stampare.
- Verificare: aprire l’anteprima o il PDF. La stessa visualizzazione la vedono i clienti nel portale.
Test
Il modulo include sei test automatizzati, che eseguiamo su un database di test prima di mettere qualsiasi cosa in produzione:
- Inserimento come sezione, comprese modalità di stampa e sottosezione
- La riga nascosta manca tra le righe da stampare
- Modalità Una riga (
summary) - Inserimento senza sezione, il comportamento originale
- La fattura recepisce il contrassegno
hide_in_report, la riga manca in_get_move_lines_to_report(), il totale della sezione la contiene ancora - La fattura in modalità Solo prezzo totale non mostra la riga nascosta tra le righe della sezione, il totale della sezione resta completo
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
--test-enable --test-tags=/sale_product_set_layout --stop-after-init
La traduzione tedesca si trova in i18n/de.po.
Limiti e avvertenza legale
- Solo layout. Il modulo non cambia il modo in cui Odoo calcola prezzi, sconti o imposte. Cambia solo ciò che viene stampato e mostrato nel portale.
- Le righe nascoste sono visibili nel backend. Il contrassegno riguarda la stampa e il portale, non la visualizzazione a schermo dei collaboratori.
- Verificare i dati di fattura. Le righe nascoste contano nel totale, ma non compaiono sul documento. Se ciò sia compatibile con i requisiti previsti per le fatture nel Suo Paese, ad esempio con la descrizione prescritta dei beni ceduti o dei servizi prestati, dovrebbe essere chiarito prima dell’impiego sulle fatture con il Suo consulente fiscale. Sui preventivi l’occultamento è meno problematico. L’articolo non costituisce consulenza legale.
- Testato per Odoo 19. Abbiamo sviluppato e testato il modulo per Odoo 19.0 Community. Utilizza opzioni di sezione presenti in questa forma in Odoo 19. Per altre versioni non lo abbiamo verificato.
Domande frequenti
A che cosa mi serve il modulo, se Odoo 19 gestisce già le sezioni?
Le opzioni di sezione esistono in Odoo stesso. Mancava il collegamento con il set di prodotti: un set doveva essere inserito automaticamente come sezione, con la modalità di stampa memorizzata nel set. Senza il modulo, dopo ogni inserimento bisogna creare e configurare a mano la riga di sezione.
Che differenza c’è tra “Nascondi prezzi” e “Nascondi composizione”?
“Nascondi prezzi” (collapse_prices) lascia le righe, ma non mostra importi, solo il totale della sezione. “Nascondi composizione” (collapse_composition) nasconde del tutto le righe: il cliente vede soltanto la sezione con il totale.
Una riga nascosta conta nel totale della fattura?
Sì. Le righe nascoste restano nel totale, nell’imposta e nella registrazione contabile. Il contrassegno riguarda solo la visualizzazione sulla stampa e nel portale clienti.
Posso ancora modificare la modalità di stampa all’inserimento?
Sì. La modalità viene proposta dal set e nella procedura guidata può essere modificata per il singolo inserimento. Il set stesso resta invariato.
Che cosa succede se disattivo Inserisci come sezione?
In tal caso la procedura guidata si comporta come nel modulo OCA sale_product_set: le righe del set vengono inserite senza sezione.
Il modulo installa inaspettatamente altri moduli?
Il modulo in sé no. Con i repository OCA possono però comparire moduli auto_install se si inseriscono interi repository nell’addons_path. Colleghi quindi solo i due moduli necessari (vedere la sezione Installazione).
Il modulo funziona con Odoo Enterprise?
Il modulo dipende solo da moduli Community (sale_management, account) e dai due moduli OCA. Lo utilizziamo su Odoo 19 Community. Su Enterprise non lo abbiamo testato.
Con quale licenza è distribuito il modulo?
Con la AGPL-3. Può utilizzarlo, adattarlo e distribuirlo, purché le modifiche restino sotto la stessa licenza. Segnalazioni di errori e suggerimenti di miglioramento sono benvenuti come issue nel repository GitHub .
Supporto da parte di Biricon IT Services
Seguiamo installazioni Odoo per piccole e medie imprese: dall’introduzione alle personalizzazioni e alle migrazioni fino alla gestione operativa. Se desidera adattare ai Suoi processi set di prodotti, modelli di preventivo o report in Odoo 19, oppure se vuole verificare un modulo come questo e portarlo in sicurezza nella Sua istanza di produzione, saremo lieti di supportarLa. Altri sviluppi interni sono disponibili nel nostro blog, ad esempio Unire le fatture in bozza e Abbinare automaticamente i pagamenti bancari alle fatture.
Aggiornato al 2 novembre 2026. Versione del modulo 19.0.1.0.0. I dati di esempio nelle figure sono scelti liberamente, importi netti.