Preskoči na sadržaj

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

2. novembra 2026. | Odoo 19 Community | Vlastiti razvoj
2. novembar 2026. by
Odoo 19 Community: setovi proizvoda kao odjeljak u ponudi – naš modul otvorenog koda Sale Product Set Layout

Modul Sale Product Set Layout za Odoo 19: setovi proizvoda kao odjeljci u ponudi s načinom ispisa, skrivenim stavkama i fiksnom cijenom

Ko u Odoou prodaje pakete, poput mjernog uređaja s priborom i montažom, radnog mjesta s hardverom i postavljanjem ili paketa održavanja s više usluga, u ponudi treba dvije stvari istovremeno: sastav mora biti naveden u narudžbi kako bi skladište, tehnička služba i služba za izdavanje računa znali šta se isporučuje. Međutim, na ispisu za kupca ne treba uvijek prikazati svaku pojedinačnu stavku s njenom cijenom.

OCA modul sale_product_set omogućava da se setovi proizvoda (sastavnice za prodaju) jednim klikom umetnu u ponudu. On ne određuje izgled ispisa. Za to smo razvili modul Sale Product Set Layout (sale_product_set_layout). Napisan je za Odoo 19 i besplatno je dostupan na GitHubu kao slobodni softver pod licencom AGPL-3:

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

Problem: setovi proizvoda bez kontrole nad izgledom ispisa

Set proizvoda u Odoou je unaprijed pripremljena lista proizvoda s količinama. Umjesto da pojedinačno tražite deset artikala, u ponudi odaberete Dodaj set i odjednom dobijete sve stavke. To štedi vrijeme i sprečava izostavljanje stavki. Modul sale_product_set iz repozitorija OCA/sale-workflow radi upravo to; sami setovi dolaze iz modula product_set u repozitoriju OCA/product-attribute.

U praksi ostaju tri nedostatka:

  • Nema jasnog grupisanja. Stavke seta u ponudi se pojavljuju kao nepovezane stavke. Nakon umetanja više se ne vidi koje stavke pripadaju zajedno, a ukupan iznos seta nigdje nije prikazan.
  • Nema kontrole nad ispisom. Ponekad kupac treba vidjeti svaku stavku s cijenom, ponekad samo cijenu paketa, a ponekad samo jedan red. To treba odrediti za svaki set ili svakog kupca, bez ponovnog ručnog podešavanja u svakoj ponudi.
  • Korekcije cijene su neugodne. Ako paket treba imati fiksnu cijenu koja odstupa od zbira cijena iz cjenovnika, potrebna je korektivna stavka. Ona spada u obračun, ali ne nužno i na papir za kupca.

Odoo 19 već sadrži elemente za uredno rješenje: odjeljke i pododjeljke među stavkama ponude s opcijama Sakrij cijene i Sakrij sastav (polja collapse_prices i collapse_composition). Modul povezuje set proizvoda s ovim standardnim opcijama umjesto da uvodi vlastiti format ispisa.

Šta modul radi

  1. Umetanje seta kao odjeljka. Čarobnjak Dodaj set kreira red odjeljka s nazivom seta, na primjer 2 × Messkit Standard, a ispod njega postavlja stavke seta. Odjeljci unutar seta postaju pododjeljci.
  2. Način ispisa za svaki set. Na setu se jednom definiše kako će se ispisivati. Pri umetanju se način ispisa u čarobnjaku može promijeniti za taj pojedinačni slučaj.
  3. Skrivanje stavki. Pojedinačne stavke mogu se označiti opcijom Ne ispisuj (na setu: Skriveno). Uključene su u ukupan iznos, ali se ne ispisuju niti prikazuju na portalu za kupce. Oznaka se prenosi s ponude na račun.
  4. Fiksna cijena po stavci seta. Stavka seta može imati fiksnu jediničnu cijenu koja zamjenjuje cijenu iz cjenovnika, na primjer za skrivenu korekciju cijene.
Bez modula (samo sale_product_set)Uz sale_product_set_layout
Stavke seta kao nepovezane stavkeRed odjeljka s nazivom seta i količinom u nazivu, sa stavkama ispod
Odjeljci u setu ostaju odjeljci i narušavaju strukturuOdjeljci u setu postaju pododjeljci (line_subsection)
Izgled ispisa uvijek je istiNačin ispisa za svaki set, uz mogućnost promjene pri umetanju
Korekcija cijene vidljiva je na ponudi i računuStavka se može sakriti, ali ostaje uključena u ukupan iznos
Cijena isključivo iz cjenovnikaOpcionalna fiksna cijena po stavci seta
Oblikovanje se primjenjuje samo na ponuduOblikovanje i skrivanje primjenjuju se na ponudu, narudžbu, račun i portal za kupce

Detaljno o tri načina ispisa

Način ispisa je izborno polje print_mode na setu proizvoda. Pri umetanju pretvara se u postavke dva standardna prekidača na redu odjeljka. Modul za to koristi jedno jednostavno i čitljivo mapiranje:

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

Tri izgleda ispisa istog seta proizvoda: svaka stavka s cijenom, samo ukupna cijena, samo jedan red

Način (print_mode)Prikaz u setuStandardna opcija u OdoouIzgled ispisa
detailedSvaka stavka s cijenomnijednaOdjeljak, sve stavke s iznosima
total_onlyStavke bez cijena, samo ukupan iznos setacollapse_prices (Sakrij cijene)Odjeljak s ukupnim iznosom, stavke samo s nazivom i količinom
summarySamo jedan red s ukupnim iznosom setacollapse_composition (Sakrij sastav)Samo red odjeljka s ukupnim iznosom

Budući da modul koristi standardne opcije Odooa 19, a ne vlastiti format ispisa, ponuda, potvrda narudžbe, račun i portal za kupce rade bez prilagođavanja predložaka izvještaja. Isto ponašanje dostupno je i onima čiji vlastiti predlošci izvještaja podržavaju opcije odjeljaka.

Skrivene stavke i fiksna cijena

Čest slučaj: paket ima cijenu nižu od zbira pojedinačnih cijena. Umjesto mijenjanja pojedinačnih cijena koristi se korektivna stavka s fiksnom cijenom (na primjer −50,00 € po setu), koja se zatim sakrije. U administracijskom dijelu obračun je jasan, a na ispisu je prikazana cijena paketa.

Korekcija cijene vidljiva je i označena u administracijskom dijelu, skrivena je u ispisu ponude, PDF-u, računu i na portalu, a ukupan iznos ostaje isti

Važno za razumijevanje: oznaka hide_in_report ne mijenja obračun. Količinu, cijenu, popust, porez, ukupne iznose i knjiženja i dalje obrađuje standardni Odoo. Stavka se samo izostavlja iz ispisa i portala.

  • Na setu proizvoda: stavka seta s oznakom Skriveno i opcionalno Fiksna cijena uz Jediničnu cijenu.
  • Na stavkama ponude i računa: kolona Ne ispisuj (hide_in_report), dostupna i za obične stavke bez seta.
  • Prenošenje: pri kreiranju računa iz narudžbe oznaka se prenosi na stavku računa. Pri kopiranju ponude ostaje sačuvana.

Tehnička implementacija

Modul je namjerno malog opsega: proširuje postojeće modele i redefiniše samo mjesta na kojima Odoo odlučuje koje se stavke ispisuju i kako se set umeće. Nema novih predložaka izvještaja, novih modela ni zahvata putem SQL-a.

Tok podataka: set proizvoda, čarobnjak Dodaj set, stavke ponude, stavke računa, PDF i portal za kupce

Prošireni modeli i polja

ModelDopuna
product.setPolja insert_as_section (logičko polje, podrazumijevano uključeno) i print_mode (izborno, obavezno polje, podrazumijevana vrijednost detailed)
product.set.linePolja hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() prenosi ih na stavku ponude
sale.product.set.wizardPolja insert_as_section, section_name, print_mode; _prepare_order_lines() kreira red odjeljka
sale.order / sale.order.linePolje hide_in_report (uz copy=True); _get_order_lines_to_report() preskače skrivene stavke; _prepare_invoice_line() prenosi 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() dodaje oznaku u podatke izvještaja

Skrivanje u ponudi

Odoo 19 određuje stavke ponude za ispis u metodi _get_order_lines_to_report(). Modul tu dodaje filter. Nije potrebno mijenjati predložak izvještaja: i predložak izvještaja ponude (sale/report/ir_actions_report_templates.xml) i predložak portala (sale/views/sale_portal_templates.xml) pozivaju ovu 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
        )

Prenošenje na račun

Pri kreiranju stavke računa iz stavke ponude (_prepare_invoice_line()) oznaka se kopira. Na strani računa u Odoou 19 postoji više puteva kojima stavke ulaze u izvještaj: glavna lista, 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), jer bi se u suprotnom skrivena stavka mogla ponovo pojaviti nekim od drugih puteva:

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 nepovezanih stavki

Čarobnjak sale.product.set.wizard iz modula sale_product_set kreira stavke ponude u metodi _prepare_order_lines(). Modul redefiniše ovu metodu. Ako je opcija Umetni kao odjeljak isključena, jednostavno poziva izvornu metodu, pa ponašanje ostaje isto kao u OCA modulu. U suprotnom prvo nastaje red odjeljka s dva standardna prekidača, a zatim slijede stavke seta. Odjeljci unutar seta postaju pododjeljci kako ne bi narušili 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 opcija Umetni kao odjeljak imaju zasebne compute metode (store=True, readonly=False). Kada bi sva tri polja koristila istu metodu, izričito proslijeđena vrijednost jednog polja resetovala bi ostala. To posebno dolazi do izražaja kada se čarobnjak kreira iz koda ili testova uz zadane vrijednosti.
  • Naziv odjeljka s količinom. Ako količina nije jednaka 1, navodi se u nazivu (2 × Messkit Standard, sa zarezom kao decimalnim separatorom), kako bi se na ispisu vidjelo da se radi o dva seta, a ne o jednom setu 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 seta nije red odjeljka ili napomene.
  • Standardni Odoo umjesto vlastitog rješenja. Način ispisa i skrivanje koriste postojeće tačke proširenja i polja Odooa 19. To olakšava nadogradnje: ako Odoo promijeni predložak izvještaja, modul ostaje funkcionalan dok god postoje odgovarajuće metode.

Instalacija bez neželjenih posljedica

Preduslovi: Odoo 19.0, sale_product_set (OCA/sale-workflow, grana 19.0) i product_set (OCA/product-attribute, grana 19.0). Sam modul zavisi od modula sale_management, sale_product_set i account.

Zamka opcije auto_install kod OCA modula. Mnogi OCA repozitoriji sadrže module s opcijom auto_install. Čim su njihove zavisnosti dostupne, Odoo ih pri sljedećoj instalaciji ili nadogradnji automatski instalira bez pitanja. Ako zbog toga cijeli OCA repozitorij dodate u addons_path produkcijske instance, možete dobiti module koje niko nije želio. U repozitoriju OCA/product-attribute to se, na primjer, odnosi na purchase_product_template_tags. Naš postupak:

  1. Kreirajte plitke klonove oba OCA repozitorija (git clone --depth 1 --branch 19.0).
  2. U zasebnom direktoriju napravite simboličke linkove samo na dva potrebna modula i dodajte taj direktorij u addons_path, a ne cijele repozitorije.
  3. Instalirajte na testnoj bazi podataka i provjerite koji moduli pri tome dobiju status instalirano.
  4. Tek zatim instalirajte u produkciji, uz --workers=0, kako se pokrenuta naredba ne bi nadmetala s aktivnim servisom za HTTP port.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init

Nova polja i prikazi nalaze se u bazi podataka nakon instalacije. Ako servis radi u više procesa, radni procesi učitavaju izmijenjeni registar putem signala baze podataka. Promjene u Python datotekama modula stupaju na snagu tek nakon ponovnog pokretanja servisa.

Korištenje korak po korak

  1. Kreiranje seta: Prodaja → Proizvodi → Setovi proizvoda. Unesite naziv seta, odaberite Umetni kao odjeljak i Način ispisa te unesite proizvode s količinama. Korektivne stavke označite opcijama Skriveno i Fiksna cijena.
  2. U ponudi: kliknite na Dodaj set, odaberite set i količinu. Naziv odjeljka i način ispisa mogu se promijeniti prije umetanja.
  3. Skrivanje pojedinačnih stavki: u ponudi ili na računu uključite opciju u koloni Ne ispisuj.
  4. Provjera: otvorite pregled ili PDF. Kupci vide isti prikaz na portalu.

Testovi

Modul sadrži šest automatizovanih testova koje izvršavamo na testnoj bazi podataka prije puštanja u produkciju:

  • Umetanje kao odjeljak, uključujući način ispisa i pododjeljak
  • Skrivena stavka nije među stavkama za ispis
  • Način Jedan red (summary)
  • Umetanje bez odjeljka, izvorno ponašanje
  • Račun preuzima oznaku hide_in_report, stavka se izostavlja iz _get_move_lines_to_report(), ali ostaje uključena u ukupan iznos odjeljka
  • Račun u načinu Samo ukupna cijena ne prikazuje skrivenu stavku među stavkama odjeljka, a ukupan iznos 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 datoteci i18n/de.po.

Ograničenja i pravna napomena

  • Samo oblikovanje. Modul ne mijenja način na koji Odoo obračunava cijene, popuste ili poreze. Mijenja samo ono što se ispisuje i prikazuje na portalu.
  • Skriveni redovi vidljivi su u administracijskom sučelju. Oznaka se odnosi na ispis i portal, a ne na prikaz koji vide zaposlenici.
  • Provjerite podatke na računu. Skriveni redovi ulaze u ukupan iznos, ali se ne prikazuju na dokumentu. Je li to u skladu sa zahtjevima za račune u vašoj zemlji, primjerice s obaveznim opisom isporučene robe ili pruženih usluga, trebate razjasniti sa svojim poreznim savjetnikom prije upotrebe na računima. Skrivanje je manje problematično na ponudama. Ovaj članak ne predstavlja pravni savjet.
  • Testirano za Odoo 19. Modul smo razvili i testirali za Odoo 19.0 Community. Koristi opcije odjeljaka koje su u ovom obliku dostupne u Odoo 19. Nismo ga provjeravali za druge verzije.

Česta pitanja

Zašto mi treba ovaj modul ako Odoo 19 već podržava odjeljke?

Opcije odjeljaka postoje u samom Odoo. Nedostajala je povezanost sa setom proizvoda: set se treba automatski umetnuti kao odjeljak, uz način ispisa podešen u setu. Bez ovog modula nakon svakog umetanja morate ručno kreirati i podesiti red odjeljka.

Koja je razlika između opcija „Sakrij cijene“ i „Sakrij sastav“?

Opcija „Sakrij cijene“ (collapse_prices) zadržava redove, ali ne prikazuje njihove iznose, nego samo ukupan iznos odjeljka. Opcija „Sakrij sastav“ (collapse_composition) potpuno skriva redove pa kupac vidi samo odjeljak s ukupnim iznosom.

Ulazi li skriveni red u ukupan iznos računa?

Da. Skriveni redovi i dalje ulaze u ukupan iznos, obračun poreza i knjiženje. Oznaka utiče samo na prikaz na ispisu i u korisničkom portalu.

Mogu li promijeniti način ispisa pri umetanju?

Da. Zadani način ispisa preuzima se iz seta, a u čarobnjaku se može promijeniti za pojedinačno umetanje. Sam set pritom ostaje neizmijenjen.

Šta se dešava ako isključim opciju Umetni kao odjeljak?

Tada čarobnjak radi kao u OCA modulu sale_product_set: redovi seta umeću se bez odjeljka.

Instalira li modul neočekivano dodatne module?

Sam modul ne instalira. Međutim, kod OCA repozitorija može doći do instaliranja modula s oznakom auto_install ako cijele repozitorije dodate u addons_path. Zato povežite samo dva potrebna modula (pogledajte odjeljak Instalacija).

Radi li modul uz Odoo Enterprise?

Modul zavisi samo od Community modula (sale_management, account) i dva OCA modula. Koristimo ga u Odoo 19 Community. Nismo ga testirali na Enterprise izdanju.

Pod kojom je licencom modul dostupan?

Pod licencom AGPL-3. Smijete ga koristiti, prilagođavati i distribuirati dok god izmjene ostaju pod istom licencom. Greške i prijedloge poboljšanja možete prijaviti kao Issue u GitHub repozitoriju .

Podrška kompanije Biricon IT Services

Pružamo podršku za Odoo instalacije malih i srednjih preduzeća: od uvođenja, prilagodbi i migracija do svakodnevnog rada. Ako želite prilagoditi setove proizvoda, predloške ponuda ili izvještaje u Odoo 19 svojim poslovnim procesima ili provjeriti modul poput ovog i sigurno ga uvesti u produkcijsko okruženje, rado ćemo vam pomoći. Više naših vlastitih rješenja pronaći ćete na blogu, naprimjer Spajanje nacrta računa i Automatsko povezivanje bankovnih uplata s računima.

Kontaktirajte nas sada

Stanje na dan: 2. novembra 2026. Verzija modula 19.0.1.0.0. Primjeri podataka na slikama proizvoljno su odabrani, iznosi su bez poreza.