Preskoči na sadržaj

Odoo 19 Community: skupovi proizvoda u ponudi kao odjeljak – naš modul otvorenog koda Sale Product Set Layout

2. studenoga 2026. | Odoo 19 Community | Vlastiti razvoj
2. studenoga 2026. od
Odoo 19 Community: skupovi proizvoda u ponudi kao odjeljak – naš modul otvorenog koda Sale Product Set Layout

Odoo 19 modul Sale Product Set Layout: skupovi proizvoda kao odjeljak u ponudi s načinom ispisa, skrivenim stavkama i fiksnom cijenom

Tko u Odoou prodaje pakete, na primjer mjerni uređaj s priborom i montažom, radno mjesto s hardverom i postavljanjem ili paket održavanja od više usluga, u ponudi istodobno treba dvije stvari: Sastav mora biti naveden u narudžbi kako bi skladište, tehnika i fakturiranje znali što se isporučuje. Na ispisu za klijenta pak ne mora u svakom slučaju biti prikazana svaka pojedinačna stavka sa svakom pojedinačnom cijenom.

OCA modul sale_product_set omogućuje umetanje skupova proizvoda (sastavnica za prodaju) u ponudu jednim klikom. O izgledu ispisa ne odlučuje. Zato smo razvili modul Sale Product Set Layout (sale_product_set_layout). Napisan je za Odoo 19 i besplatno je dostupan na GitHubu kao slobodan softver pod licencijom AGPL-3:

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

Problem: skupovi proizvoda bez utjecaja na izgled ispisa

Skup proizvoda u Odoou unaprijed je pripremljen popis proizvoda s količinama. Umjesto da se deset artikala traži pojedinačno, u ponudi se odabere Dodaj skup i dobiju se sve stavke odjednom. To štedi vrijeme i sprječava zaboravljene stavke. Modul sale_product_set iz repozitorija OCA/sale-workflow radi upravo to; sami skupovi potječu iz modula product_set u repozitoriju OCA/product-attribute.

U praksi ostaju tri nedostatka:

  • Nema prepoznatljivog grupiranja. Stavke skupa završavaju kao samostalne stavke u ponudi. Nakon umetanja više se ne vidi koje stavke pripadaju zajedno, a ukupni iznos skupa nigdje ne stoji.
  • Nema utjecaja na ispis. Ponekad klijent treba vidjeti svaku stavku s cijenom, ponekad samo cijenu paketa, ponekad samo jednu jedinu stavku. To je odluka po skupu ili po klijentu, a ne nešto što se za svaku ponudu iznova rješava ručno.
  • Ispravci cijena su neugodni. Ako paket treba imati fiksnu cijenu koja odstupa od zbroja cjenikovskih cijena, potrebna je stavka za korekciju. Ona pripada izračunu, ali ne nužno i na papir za klijenta.

Odoo 19 donosi gradivne elemente za čisto rješenje: odjeljke i pododjeljke u stavkama ponude s opcijama Sakrij cijene i Sakrij sastav (polja collapse_prices i collapse_composition). Modul povezuje skup proizvoda s tim standardnim opcijama umjesto da izmišlja vlastiti format ispisa.

Što modul radi

  1. Umetanje skupa kao odjeljka. Čarobnjak Dodaj skup stvara stavku odjeljka s nazivom skupa, na primjer 2 × Messkit Standard, a stavke skupa postavlja ispod nje. Odjeljci unutar skupa postaju pododjeljci.
  2. Način ispisa po skupu. U skupu se jednom određuje kako će se ispisivati. Pri umetanju se način u čarobnjaku može promijeniti za taj jedan slučaj.
  3. Skrivanje stavki. Pojedine stavke mogu se označiti kao Ne ispisuj (u skupu: Skriveno). Ulaze u zbroj, ali se ne ispisuju niti prikazuju u klijentskom portalu. Oznaka se s ponude prenosi na račun.
  4. Fiksna cijena po stavci skupa. Stavka skupa može imati fiksnu jediničnu cijenu koja zamjenjuje cijenu iz cjenika, na primjer za skriveni ispravak cijene.
Bez modula (samo sale_product_set)S modulom sale_product_set_layout
Stavke skupa kao samostalne stavkeStavka odjeljka s nazivom skupa, količina u nazivu, stavke ispod
Odjeljci u skupu ostaju odjeljci i razbijaju strukturuOdjeljci u skupu postaju pododjeljci (line_subsection)
Izgled ispisa uvijek istiNačin ispisa po skupu, promjenjiv pri umetanju
Ispravak cijene vidljiv na ponudi i računuStavku je moguće sakriti, a ipak ulazi u zbroj
Cijena samo iz cjenikaOpcionalno fiksna cijena po stavci skupa
Izgled završava na ponudiIzgled i skrivanje vrijede za ponudu, narudžbu, račun i klijentski portal

Tri načina ispisa podrobno

Način ispisa je polje za odabir print_mode na skupu proizvoda. Pri umetanju se prevodi u dva standardna prekidača stavke odjeljka. Za to u modulu postoji jedno jednostavno i lako čitljivo pridruživanje:

# način ispisa -> (collapse_prices, collapse_composition) stavke odjeljka
PRINT_MODE_FLAGS = {
    "detailed":   (False, False),
    "total_only": (True,  False),
    "summary":    (False, True),
}

Tri izgleda ispisa istog skupa proizvoda: svaka stavka s cijenom, samo ukupna cijena, jedna jedina stavka

Način (print_mode)Prikaz u skupuStandardna opcija u OdoouIzgled ispisa
detailedSvaka stavka s cijenomnemaOdjeljak, sve stavke s iznosom
total_onlyStavke bez cijena, samo zbroj skupacollapse_prices (Sakrij cijene)Odjeljak sa zbrojem, stavke samo s nazivom i količinom
summarySamo jedna stavka sa zbrojem skupacollapse_composition (Sakrij sastav)Samo stavka odjeljka sa zbrojem

Budući da modul koristi standardne opcije Odoo 19, a ne vlastiti format ispisa, ponuda, potvrda narudžbe, račun i klijentski portal rade bez prilagođenih predložaka izvješća. Tko ima vlastite predloške izvješća koji uzimaju u obzir opcije odjeljaka, dobit će isto ponašanje.

Skrivene stavke i fiksna cijena

Čest slučaj: paket ima cijenu paketa koja je niža od zbroja pojedinačnih cijena. Umjesto da se pojedinačne cijene izobličuju, uzima se korekcijska stavka s fiksnom cijenom (na primjer −50,00 € po skupu) i sakriva se. U backendu je račun razumljiv, a na ispisu stoji cijena paketa.

Ispravak cijene vidljiv i označen u backendu, na ispisu ponude, PDF-u, računu i portalu nije vidljiv, zbroj je identičan

Važno za razumijevanje: oznaka hide_in_report ne mijenja ništa u izračunu. Količina, cijena, popust, porez, zbrojevi i knjiženja nastaju nepromijenjeno u standardnom Odoou. Stavka samo nedostaje na ispisu i u portalu.

  • Na skupu proizvoda: stavka skupa s opcijom Skriveno i po želji Fiksna cijena zajedno s Jediničnom cijenom.
  • Na stavkama ponude i računa: stupac Ne ispisuj (hide_in_report), također za obične stavke bez skupa.
  • Prijenos: pri izradi računa iz narudžbe oznaka se prenosi na stavku računa. Pri kopiranju ponude ostaje sačuvana.

Tehnička izvedba

Modul je namjerno malen: proširuje postojeće modele i nadjačava samo ona mjesta na kojima Odoo odlučuje koje se stavke ispisuju i kako se skup umeće. Nema novih predložaka izvješća, novih modela ni SQL zahvata.

Tijek podataka: skup proizvoda, čarobnjak Dodaj skup, stavke ponude, stavke računa, PDF i klijentski portal

Prošireni modeli i polja

ModelDopuna
product.setPolja insert_as_section (Boolean, prema zadanom uključeno) i print_mode (izbor, obavezno polje, zadano detailed)
product.set.linePolja hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() prosljeđuje ih stavci ponude
sale.product.set.wizardPolja insert_as_section, section_name, print_mode; _prepare_order_lines() gradi stavku odjeljka
sale.order / sale.order.linePolje hide_in_report (s copy=True); _get_order_lines_to_report() preskače skrivene stavke; _prepare_invoice_line() prosljeđuje oznaku
account.move / account.move.linePolje hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() i _get_subsection_lines() filtriraju skrivene stavke; _get_aml_vals() prenosi oznaku u podatke izvješća

Skrivanje u ponudi

Odoo 19 stavke ponude za ispis određuje u metodi _get_order_lines_to_report(). Modul na tom mjestu dodaje filtar. Zahvat u predložak izvješća nije potreban: i predložak izvješća ponude (sale/report/ir_actions_report_templates.xml) i predložak portala (sale/views/sale_portal_templates.xml) pozivaju tu metodu:

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
        )

Prijenos na račun

Pri izradi stavke računa iz stavke ponude (_prepare_invoice_line()) oznaka se kopira. Na strani računa Odoo 19 ima više putova kojima stavke dospijevaju u izvješće: glavni popis, stavke odjeljka i stavke pododjeljka. Zato modul filtrira na više mjesta (_get_move_lines_to_report() na dokumentu, _get_detailed_lines() i _get_subsection_lines() na stavci), inače bi se skrivena stavka mogla ponovno pojaviti nekim od drugih putova:

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

Čarobnjak: odjeljak umjesto samostalnih stavki

Čarobnjak sale.product.set.wizard iz modula sale_product_set stvara stavke ponude u metodi _prepare_order_lines(). Modul tu metodu nadjačava. Ako je Umetni kao odjeljak isključeno, jednostavno poziva izvornu metodu, a ponašanje ostaje kao u OCA modulu. U suprotnom najprije nastaje stavka odjeljka s dva standardna prekidača, a zatim slijede stavke skupa. Odjeljci unutar skupa postaju pododjeljci kako ne bi razbijali glavnu 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

Odluke koje se ne vide

  • Odvojene metode izračuna u čarobnjaku. Naziv odjeljka, način ispisa i Umetni kao odjeljak imaju svaki svoju compute metodu (store=True, readonly=False). Kad bi sva tri dijelila istu metodu, izričito proslijeđena vrijednost jednog polja poništila bi ostala. To se uočava osobito kad se čarobnjak stvara iz koda ili testova s vrijednostima.
  • Naziv odjeljka s količinom. Ako je količina različita od 1, navodi se u nazivu (2 × Messkit Standard, sa zarezom kao decimalnim separatorom), kako bi se na ispisu prepoznalo da se radi o dva skupa, a ne o jednom skupu s dvostrukom količinom.
  • Fiksna cijena samo na stavkama proizvoda. prepare_sale_order_line_values() postavlja hide_in_report i price_unit samo ako stavka skupa nije stavka odjeljka ni bilješke.
  • Standardni Odoo umjesto vlastite izrade. Način ispisa i skrivanje koriste postojeće kuke i polja Odoo 19. To čini ažuriranja podnošljivima: ako Odoo promijeni predložak izvješća, modul ostaje valjan dok god te metode postoje.

Instalacija bez neželjenih nuspojava

Preduvjeti: Odoo 19.0, sale_product_set (OCA/sale-workflow, grana 19.0) i product_set (OCA/product-attribute, grana 19.0). Sam modul ovisi o modulima sale_management, sale_product_set i account.

Zamka auto_install kod OCA modula. Mnogi OCA repozitoriji sadrže module s opcijom auto_install. Čim su dostupne njihove ovisnosti, Odoo ih pri sljedećoj instalaciji ili ažuriranju bez pitanja instalira zajedno. Tko zato cijeli OCA repozitorij stavi u addons_path produkcijske instance, možda dobije module koje nitko nije želio. Kod OCA/product-attribute to se na primjer odnosi na purchase_product_template_tags. Naš postupak:

  1. Oba OCA repozitorija klonirati plitko (git clone --depth 1 --branch 19.0).
  2. U zasebnom direktoriju simboličkom vezom povezati samo dva potrebna modula i taj direktorij uključiti u addons_path, a ne cijele repozitorije.
  3. Instalirati na testnoj bazi podataka i provjeriti koji moduli pritom dobiju status instalirano.
  4. Tek tada instalirati produkcijski, s --workers=0, kako poziv ne bi s pokrenutom uslugom konkurirao za HTTP port.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init

Nova polja i prikazi nakon instalacije nalaze se u bazi podataka. Ako usluga radi u višeprocesnom načinu, workeri ponovno učitavaju promijenjeni registar putem signala baze podataka. Promjene u Python datotekama modula postaju djelotvorne tek nakon ponovnog pokretanja usluge.

Korištenje korak po korak

  1. Izrada skupa: Prodaja → Proizvodi → Skupovi proizvoda. Imenovati skup, odabrati Umetni kao odjeljak i Način ispisa, unijeti proizvode s količinama. Korekcijske stavke označiti opcijama Skriveno i Fiksna cijena.
  2. U ponudi: kliknuti na Dodaj skup, odabrati skup i količinu. Naziv odjeljka i način ispisa mogu se promijeniti prije umetanja.
  3. Skrivanje pojedinih stavki: u ponudi ili računu aktivirati stupac Ne ispisuj.
  4. Provjera: otvoriti pregled ili PDF. Isti prikaz klijenti vide u portalu.

Testovi

Modul donosi šest automatiziranih testova koje izvodimo na testnoj bazi podataka prije nego što bilo što ode u produkciju:

  • Umetanje kao odjeljak uključujući način ispisa i pododjeljak
  • Skrivena stavka nedostaje među stavkama za ispis
  • Način Jedna stavka (summary)
  • Umetanje bez odjeljka, izvorno ponašanje
  • Račun preuzima oznaku hide_in_report, stavka nedostaje u _get_move_lines_to_report(), zbroj odjeljka i dalje sadrži
  • Račun u načinu Samo ukupna cijena ne prikazuje skrivenu stavku među stavkama odjeljka, zbroj odjeljka ostaje potpun
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
         --test-enable --test-tags=/sale_product_set_layout --stop-after-init

Njemački prijevod nalazi se u i18n/de.po.

Ograničenja i pravna napomena

  • Samo izgled. Modul ne mijenja način na koji Odoo izračunava cijene, popuste ili poreze. Mijenja samo ono što se ispisuje i prikazuje u portalu.
  • Skrivene stavke vidljive su u backendu. Oznaka se odnosi na ispis i portal, a ne na prikaz na zaslonu zaposlenika.
  • Provjerite podatke na računu. Skrivene stavke ulaze u zbroj, ali ne stoje na dokumentu. Je li to u skladu sa zahtjevima za račune u vašoj zemlji, na primjer s propisanim opisom isporučenih predmeta ili pruženih usluga, trebali biste razjasniti sa svojim poreznim savjetnikom prije uporabe na računima. Na ponudama je skrivanje manje problematično. Članak nije pravni savjet.
  • Testirano za Odoo 19. Modul smo razvili i testirali za Odoo 19.0 Community. Koristi opcije odjeljaka koje u ovom obliku postoje u Odoou 19. Za druge verzije nismo ga provjeravali.

Česta pitanja

Čemu mi služi modul ako Odoo 19 već ima odjeljke?

Opcije odjeljaka postoje u samom Odoou. Nedostajala je veza sa skupom proizvoda: skup bi se trebao automatski umetnuti kao odjeljak, s načinom ispisa pohranjenim u skupu. Bez modula nakon svakog umetanja stavku odjeljka treba ručno izraditi i konfigurirati.

Po čemu se razlikuju „Sakrij cijene“ i „Sakrij sastav“?

„Sakrij cijene“ (collapse_prices) ostavlja stavke, ali ne prikazuje iznose, samo zbroj na odjeljku. „Sakrij sastav“ (collapse_composition) potpuno skriva stavke, klijent vidi samo odjeljak sa zbrojem.

Ulazi li skrivena stavka u zbroj računa?

Da. Skrivene stavke ostaju u zbroju, porezu i knjiženju. Oznaka se odnosi samo na prikaz na ispisu i u klijentskom portalu.

Mogu li način ispisa još promijeniti pri umetanju?

Da. Način se preuzima kao zadana vrijednost iz skupa, a u čarobnjaku se može promijeniti za pojedino umetanje. Sam skup pritom ostaje nepromijenjen.

Što se događa ako isključim Umetni kao odjeljak?

Tada se čarobnjak ponaša kao u OCA modulu sale_product_set: stavke skupa umeću se bez odjeljka.

Instalira li modul neočekivano dodatne module?

Sam modul ne. No kod OCA repozitorija može doći do modula s auto_install ako se cijeli repozitoriji stave u addons_path. Zato povežite samo dva potrebna modula (vidi odjeljak Instalacija).

Radi li modul s Odoo Enterprise?

Modul ovisi samo o Community modulima (sale_management, account) i o dva OCA modula. Koristimo ga na Odoo 19 Community. Na Enterpriseu ga nismo testirali.

Pod kojom licencijom je modul?

Pod AGPL-3. Smijete ga koristiti, prilagođavati i dalje distribuirati, pod uvjetom da izmjene ostanu pod istom licencijom. Greške i prijedlozi poboljšanja dobrodošli su kao issue u GitHub repozitoriju .

Podrška tvrtke Biricon IT Services

Brinemo se za Odoo instalacije malih i srednjih poduzeća: od uvođenja, preko prilagodbi i migracija, do tekućeg rada. Ako želite prilagoditi skupove proizvoda, predloške ponuda ili izvješća u Odoou 19 svojim procesima ili ako želite provjeriti modul poput ovog i sigurno ga uvesti u svoju produkcijsku instancu, rado ćemo vas podržati. Dodatne vlastite razvoje pronaći ćete u našem blogu, na primjer Spajanje nacrta računa i Automatsko povezivanje bankovnih uplata s računima.

Javite nam se odmah

Stanje: 2. studenoga 2026. Verzija modula 19.0.1.0.0. Primjeri podataka na slikama proizvoljno su odabrani, iznosi su neto.