Skip to Content

Odoo 19 Community: produktsett i tilbudet som seksjon – vår åpen kildekode-modul Sale Product Set Layout

2. november 2026 | Odoo 19 Community | Egenutvikling
2. november 2026 etter
Odoo 19 Community: produktsett i tilbudet som seksjon – vår åpen kildekode-modul Sale Product Set Layout

Odoo 19-modulen Sale Product Set Layout: produktsett som seksjon i tilbudet med utskriftsmodus, skjulte linjer og fastpris

Den som selger pakker i Odoo, for eksempel et måleinstrument med tilbehør og montering, en arbeidsplass med maskinvare og oppsett eller en vedlikeholdspakke av flere tjenester, trenger to ting samtidig i tilbudet: Sammensetningen må stå i ordren, slik at lager, teknikk og fakturering vet hva som skal leveres. På utskriften til kunden skal derimot ikke alltid hver enkelt post med hver enkelt pris vises.

OCA-modulen sale_product_set gjør det mulig å sette inn produktsett (stykklister for salg) i et tilbud med ett klikk. Hvordan utskriften ser ut, avgjør den ikke. Til det har vi utviklet modulen Sale Product Set Layout (sale_product_set_layout). Den er skrevet for Odoo 19 og er gratis tilgjengelig på GitHub som fri programvare under AGPL-3-lisensen:

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

Problemet: produktsett uten innflytelse på utskriften

Et produktsett i Odoo er en forhåndsdefinert liste over produkter med mengder. I stedet for å lete opp ti artikler enkeltvis, velger man Legg til sett i tilbudet og får alle linjene på én gang. Det sparer tid og hindrer at poster blir glemt. Modulen sale_product_set fra repositoryet OCA/sale-workflow gjør akkurat dette; selve settene kommer fra product_set i repositoryet OCA/product-attribute.

I praksis gjenstår tre hull:

  • Ingen synlig gruppering. Settlinjene havner som løse poster i tilbudet. Etter innsettingen ser man ikke lenger hvilke linjer som hører sammen, og totalsummen for settet står ingen steder.
  • Ingen innflytelse på utskriften. Noen ganger skal kunden se hver post med pris, noen ganger bare pakkeprisen, noen ganger bare én enkelt linje. Det er en avgjørelse per sett eller per kunde, ikke noe som skal gjøres for hånd i hvert tilbud.
  • Priskorreksjoner er uelegante. Skal en pakke ha en fast pris som avviker fra summen av listeprisene, trengs det en korreksjonslinje. Den hører hjemme i beregningen, men ikke nødvendigvis på kundens papir.

Odoo 19 har byggeklossene som trengs for å løse dette på en ryddig måte: seksjoner og underseksjoner i tilbudslinjer med alternativene Skjul priser og Skjul sammensetning (feltene collapse_prices og collapse_composition). Modulen kobler produktsettet til disse standardalternativene i stedet for å finne opp et eget utskriftsformat.

Hva modulen gjør

  1. Sett inn sett som seksjon. Veiviseren Legg til sett oppretter en seksjonslinje med settets navn, for eksempel 2 × Messkit Standard, og plasserer settlinjene under. Seksjoner inne i settet blir til underseksjoner.
  2. Utskriftsmodus per sett. I settet fastsettes det én gang hvordan det skal skrives ut. Ved innsetting kan modusen endres i veiviseren for akkurat dette tilfellet.
  3. Skjule linjer. Enkeltlinjer kan merkes som Ikke skriv ut (på settet: Skjult). De regnes med i summen, men blir verken skrevet ut eller vist i kundeportalen. Merket følger med fra tilbudet til fakturaen.
  4. Fastpris per settlinje. En settlinje kan ha en fast enhetspris som erstatter prisen fra prislisten, for eksempel for en skjult priskorreksjon.
Uten modulen (kun sale_product_set)Med sale_product_set_layout
Settlinjer som løse posterSeksjonslinje med settnavn, mengde i navnet, linjer under
Seksjoner i settet forblir seksjoner og ødelegger strukturenSeksjoner i settet blir underseksjoner (line_subsection)
Utskriften er alltid likUtskriftsmodus per sett, kan endres ved innsetting
Priskorreksjon synlig på tilbud og fakturaLinjen kan skjules, men regnes med i summen
Pris kun fra prislistenValgfri fastpris per settlinje
Layouten ender ved tilbudetLayout og skjuling gjelder for tilbud, ordre, faktura og kundeportal

De tre utskriftsmodusene i detalj

Utskriftsmodusen er et valgfelt print_mode på produktsettet. Ved innsetting oversettes den til de to standardbryterne på seksjonslinjen. Til dette finnes det i modulen én enkelt, lettlest tilordning:

# Druckmodus -> (collapse_prices, collapse_composition) der Abschnittszeile
PRINT_MODE_FLAGS = {
    "detailed":   (False, False),
    "total_only": (True,  False),
    "summary":    (False, True),
}

Tre utskriftsbilder av samme produktsett: hver linje med pris, kun totalpris, én enkelt linje

Modus (print_mode)Visning i settetStandardalternativ i OdooUtskrift
detailedHver linje med prisingenSeksjon, alle linjer med beløp
total_onlyLinjer uten priser, kun settsumcollapse_prices (Skjul priser)Seksjon med sum, linjer kun med betegnelse og mengde
summaryKun én linje med settsumcollapse_composition (Skjul sammensetning)Kun seksjonslinjen med summen

Fordi modulen bruker standardalternativene i Odoo 19 og ikke et eget utskriftsformat, fungerer tilbud, ordrebekreftelse, faktura og kundeportal uten tilpassede rapportmaler. Den som har egne rapportmaler som tar hensyn til seksjonsalternativene, får samme oppførsel.

Skjulte linjer og fastpris

Et vanlig tilfelle: En pakke har en pakkepris som ligger under summen av enkeltprisene. I stedet for å bøye enkeltprisene bruker man en korreksjonslinje med fastpris (for eksempel −50,00 € per sett) og skjuler den. I backend er fakturaen etterprøvbar, på utskriften står pakkeprisen.

Priskorreksjon synlig og merket i backend, ikke synlig på utskrift av tilbud, PDF, faktura og portal, summen er identisk

Viktig for forståelsen: Merket hide_in_report endrer ingenting ved beregningen. Mengde, pris, rabatt, skatt, summer og posteringer oppstår uendret gjennom standard Odoo. Linjen mangler bare på utskriften og i portalen.

  • På produktsettet: settlinje med Skjult og eventuelt Fastpris med Enhetspris.
  • På tilbuds- og fakturalinjer: kolonnen Ikke skriv ut (hide_in_report), også for vanlige linjer uten sett.
  • Videreføring: Når fakturaen opprettes fra ordren, overføres merket til fakturalinjen. Ved kopiering av et tilbud beholdes det.

Teknisk gjennomføring

Modulen er bevisst liten: Den utvider eksisterende modeller og overstyrer bare de stedene der Odoo avgjør hvilke linjer som skrives ut og hvordan et sett settes inn. Det finnes ingen nye rapportmaler, ingen nye modeller og ingen SQL-inngrep.

Dataflyt: produktsett, veiviseren Legg til sett, tilbudslinjer, fakturalinjer, PDF og kundeportal

Utvidede modeller og felter

ModellTillegg
product.setFeltene insert_as_section (boolsk, på som standard) og print_mode (valg, obligatorisk felt, standard detailed)
product.set.lineFeltene hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() sender dem videre til tilbudslinjen
sale.product.set.wizardFeltene insert_as_section, section_name, print_mode; _prepare_order_lines() bygger seksjonslinjen
sale.order / sale.order.lineFeltet hide_in_report (med copy=True); _get_order_lines_to_report() hopper over skjulte linjer; _prepare_invoice_line() sender merket videre
account.move / account.move.lineFeltet hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() og _get_subsection_lines() filtrerer bort skjulte linjer; _get_aml_vals() fører merket inn i rapportdataene

Skjuling i tilbudet

Odoo 19 finner tilbudslinjene som skal skrives ut i _get_order_lines_to_report(). Modulen henger på et filter der. Et inngrep i rapportmalen er ikke nødvendig: Både rapportmalen for tilbudet (sale/report/ir_actions_report_templates.xml) og portalmalen (sale/views/sale_portal_templates.xml) kaller denne metoden:

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
        )

Videreføring til fakturaen

Når fakturalinjen opprettes fra tilbudslinjen (_prepare_invoice_line()), kopieres merket. På fakturasiden finnes det i Odoo 19 flere veier som linjer kommer inn i rapporten på: hovedlisten, linjene i en seksjon og linjene i en underseksjon. Derfor filtrerer modulen på flere steder (_get_move_lines_to_report() på dokumentet, _get_detailed_lines() og _get_subsection_lines() på linjen), ellers kunne en skjult linje dukke opp igjen via en av de andre veiene:

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

Veiviseren: seksjon i stedet for løse linjer

Veiviseren sale.product.set.wizard fra sale_product_set genererer tilbudslinjene i _prepare_order_lines(). Modulen overstyrer denne metoden. Er Sett inn som seksjon slått av, kaller den bare den opprinnelige metoden, og oppførselen blir som i OCA-modulen. Ellers opprettes først seksjonslinjen med de to standardbryterne, deretter følger settlinjene. Seksjoner inne i settet blir til underseksjoner, slik at de ikke ødelegger hovedstrukturen:

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

Avgjørelser man ikke ser

  • Separate beregningsmetoder i veiviseren. Seksjonsnavn, utskriftsmodus og Sett inn som seksjon har hver sin compute-metode (store=True, readonly=False). Hvis alle tre hadde samme metode, ville en eksplisitt oppgitt verdi for ett felt tilbakestille de andre. Det merkes særlig når veiviseren opprettes med verdier fra kode eller tester.
  • Seksjonsnavn med mengde. Ved en mengde som ikke er 1 står den i navnet (2 × Messkit Standard, med komma som desimaltegn), slik at man på utskriften ser at det er to sett og ikke ett sett med dobbel mengde.
  • Fastpris kun på produktlinjer. prepare_sale_order_line_values() setter hide_in_report og price_unit bare hvis settlinjen ikke er en seksjons- eller notatlinje.
  • Standard Odoo i stedet for hjemmebygd løsning. Utskriftsmodus og skjuling bruker eksisterende hooks og felter i Odoo 19. Det gjør oppdateringer håndterbare: Endrer Odoo rapportmalen, forblir modulen gyldig så lenge metodene finnes.

Installasjon uten uønskede bivirkninger

Forutsetninger: Odoo 19.0, sale_product_set (OCA/sale-workflow, gren 19.0) og product_set (OCA/product-attribute, gren 19.0). Selve modulen avhenger av sale_management, sale_product_set og account.

auto_install-fellen i OCA-moduler. Mange OCA-repositorier inneholder moduler med auto_install. Så snart avhengighetene deres er til stede, installerer Odoo dem uten å spørre ved neste installasjon eller oppdatering. Den som derfor legger hele OCA-repositoriet i addons_path til en produksjonsinstans, får kanskje moduler som ingen ønsket. For OCA/product-attribute gjelder det for eksempel purchase_product_template_tags. Vår fremgangsmåte:

  1. Klon de to OCA-repositoriene grunt (git clone --depth 1 --branch 19.0).
  2. Lenk i en egen katalog kun de to nødvendige modulene via symlenke og ta denne katalogen med i addons_path, ikke hele repositoriene.
  3. Installer på en testdatabase og kontroller hvilke moduler som får status installert i den forbindelse.
  4. Først deretter installeres i produksjon, med --workers=0, slik at kallet ikke konkurrerer med den kjørende tjenesten om HTTP-porten.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init

Nye felter og visninger finnes i databasen etter installasjonen. Kjører tjenesten i flerprosessmodus, laster workerne den endrede registryen på nytt via databasesignalet. Endringer i modulens Python-filer trer først i kraft etter en omstart av tjenesten.

Bruk steg for steg

  1. Opprett sett: Salg → Produkter → Produktsett. Gi settet navn, velg Sett inn som seksjon og Utskriftsmodus, og før inn produktene med mengder. Gi korreksjonslinjer Skjult og Fastpris.
  2. I tilbudet: Klikk på Legg til sett, velg sett og mengde. Seksjonsnavn og utskriftsmodus kan endres før innsetting.
  3. Skjule enkeltlinjer: Aktiver kolonnen Ikke skriv ut i tilbudet eller fakturaen.
  4. Kontrollere: Åpne forhåndsvisningen eller PDF-en. Kundene ser samme visning i portalen.

Tester

Modulen leveres med seks automatiserte tester som vi kjører mot en testdatabase før noe går i produksjon:

  • Innsetting som seksjon inkludert utskriftsmodus og underseksjon
  • Skjult linje mangler i linjene som skal skrives ut
  • Modus Én linje (summary)
  • Innsetting uten seksjon, den opprinnelige oppførselen
  • Fakturaen overtar merket hide_in_report, linjen mangler i _get_move_lines_to_report(), seksjonssummen inneholder den fortsatt
  • Faktura i modus Kun totalpris viser ikke den skjulte linjen i seksjonslinjene, seksjonssummen forblir fullstendig
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
         --test-enable --test-tags=/sale_product_set_layout --stop-after-init

Den tyske oversettelsen ligger i i18n/de.po.

Begrensninger og juridisk merknad

  • Kun layout. Modulen endrer ikke hvordan Odoo beregner priser, rabatter eller skatt. Den endrer bare hva som skrives ut og vises i portalen.
  • Skjulte linjer er synlige i backend. Merket gjelder utskrift og portal, ikke de ansattes skjermvisning.
  • Kontroller fakturaopplysningene. Skjulte linjer regnes med i summen, men står ikke på dokumentet. Om dette er forenlig med kravene til fakturaer i ditt land, for eksempel med den foreskrevne beskrivelsen av leverte varer eller utførte tjenester, bør du avklare før bruk på fakturaer med din skatterådgiver eller revisor. På tilbud er skjuling mindre problematisk. Innlegget er ikke juridisk rådgivning.
  • Testet for Odoo 19. Vi har utviklet og testet modulen for Odoo 19.0 Community. Den bruker seksjonsalternativer som finnes i denne formen i Odoo 19. For andre versjoner har vi ikke testet den.

Ofte stilte spørsmål

Hvorfor trenger jeg modulen når Odoo 19 allerede kan seksjoner?

Seksjonsalternativene finnes i selve Odoo. Det som manglet, var koblingen til produktsettet: Et sett skulle automatisk settes inn som seksjon, med utskriftsmodusen som er lagret i settet. Uten modulen må man opprette og konfigurere seksjonslinjen for hånd etter hver innsetting.

Hva er forskjellen på «Skjul priser» og «Skjul sammensetning»?

«Skjul priser» (collapse_prices) lar linjene stå, men viser ingen beløp, bare summen på seksjonen. «Skjul sammensetning» (collapse_composition) skjuler linjene helt, og kunden ser bare seksjonen med summen.

Regnes en skjult linje med i fakturasummen?

Ja. Skjulte linjer forblir i sum, skatt og postering. Merket gjelder bare visningen på utskriften og i kundeportalen.

Kan jeg fortsatt endre utskriftsmodusen ved innsetting?

Ja. Modusen kommer som forhåndsverdi fra settet, og i veiviseren kan den endres for den aktuelle innsettingen. Selve settet forblir uendret.

Hva skjer hvis jeg slår av Sett inn som seksjon?

Da oppfører veiviseren seg som i OCA-modulen sale_product_set: Settlinjene settes inn uten seksjon.

Installerer modulen uventet flere moduler?

Selve modulen gjør ikke det. Med OCA-repositorier kan det imidlertid komme auto_install-moduler hvis man legger hele repositorier i addons_path. Lenk derfor bare de to nødvendige modulene (se avsnittet Installasjon).

Fungerer modulen med Odoo Enterprise?

Modulen avhenger bare av Community-moduler (sale_management, account) og de to OCA-modulene. Vi bruker den på Odoo 19 Community. På Enterprise har vi ikke testet den.

Under hvilken lisens står modulen?

Under AGPL-3. Du kan bruke den, tilpasse den og gi den videre, så lenge endringer forblir under samme lisens. Feil og forbedringsforslag er velkomne som issue i GitHub-repositoriet .

Støtte fra Biricon IT Services

Vi drifter Odoo-installasjoner for små og mellomstore bedrifter: fra innføring via tilpasninger og migreringer til løpende drift. Hvis du vil tilpasse produktsett, tilbudsmaler eller rapporter i Odoo 19 til arbeidsprosessene dine, eller hvis du vil få en modul som denne vurdert og trygt satt i produksjon, bistår vi deg gjerne. Flere egenutviklinger finner du i bloggen vår, for eksempel Slå sammen fakturakladder og Tilordne bankbetalinger automatisk til fakturaer.

Ta kontakt nå

Status: 2. november 2026. Modulversjon 19.0.1.0.0. Eksempeldataene i illustrasjonene er fritt valgt, beløp netto.