Kush shet në Odoo paketa, për shembull një aparat matës me aksesorë dhe montim, një vend pune me harduer dhe konfigurim ose një paketë mirëmbajtjeje me disa shërbime, ka nevojë në ofertë për dy gjëra njëkohësisht: Përbërja duhet të figurojë në porosi, që magazina, teknika dhe faturimi të dinë çfarë dorëzohet. Por në printimin për klientin nuk duhet që në çdo rast të shfaqet çdo pozicion i veçantë me çdo çmim të veçantë.
Moduli OCA sale_product_set lejon që setet e produkteve (lista materialesh për shitje) të futen në një ofertë me një klikim. Pamjen e printuar nuk e vendos ai. Për këtë kemi zhvilluar modulin Sale Product Set Layout (sale_product_set_layout). Është shkruar për Odoo 19 dhe është i disponueshëm falas në GitHub si softuer i lirë nën licencën AGPL-3:
github.com/Alex0176/odoo-sale-product-set-layout
Përmbajtja e këtij artikulli
- Problemi: sete produktesh pa ndikim në pamjen e printuar
- Çfarë bën moduli
- Tri mënyrat e printimit në detaje
- Rreshta të fshehur dhe çmim fiks
- Zbatimi teknik
- Instalim pa efekte anësore të padëshiruara
- Përdorimi hap pas hapi
- Testet
- Kufizimet dhe shënim ligjor
- Pyetje të shpeshta
- Mbështetje nga Biricon IT Services
Problemi: sete produktesh pa ndikim në pamjen e printuar
Një set produktesh në Odoo është një listë e përgatitur produktesh me sasi. Në vend që të kërkoni dhjetë artikuj një nga një, në ofertë zgjidhni Shto set dhe merrni të gjithë rreshtat menjëherë. Kjo kursen kohë dhe parandalon harrimin e pozicioneve. Moduli sale_product_set nga repozitori OCA/sale-workflow bën pikërisht këtë; vetë setet vijnë nga product_set në repozitorin OCA/product-attribute.
Në praktikë mbeten tri boshllëqe:
- Asnjë grupim i dallueshëm. Rreshtat e setit hyjnë në ofertë si pozicione të shkëputura. Pas futjes nuk shihet më se cilët rreshta bashkëshkojnë dhe totali i setit nuk shfaqet askund.
- Asnjë ndikim në printim. Herë klienti duhet të shohë çdo pozicion me çmim, herë vetëm çmimin e paketës, herë vetëm një rresht të vetëm. Kjo është një vendim për çdo set ose për çdo klient, jo çdo herë nga e para me dorë për çdo ofertë.
- Korrigjimet e çmimit janë të pakëndshme. Nëse një paketë duhet të ketë çmim fiks që ndryshon nga shuma e çmimeve të listës, nevojitet një rresht korrigjimi. Ai i përket llogaritjes, por jo domosdoshmërisht letrës së klientit.
Odoo 19 sjell përbërësit për ta zgjidhur këtë në mënyrë të pastër: seksione dhe nënseksione në rreshtat e ofertës me opsionet Fshih çmimet dhe Fshih përbërjen (fushat collapse_prices dhe collapse_composition). Moduli e lidh setin e produkteve me këto opsione standarde, në vend që të shpikë një format të vetin printimi.
Çfarë bën moduli
- Futja e setit si seksion. Asistenti Shto set krijon një rresht seksioni me emrin e setit, për shembull 2 × Messkit Standard, dhe vendos rreshtat e setit poshtë tij. Seksionet brenda setit bëhen nënseksione.
- Mënyra e printimit për çdo set. Në set përcaktohet një herë se si printohet. Gjatë futjes, mënyra mund të ndryshohet në asistent për atë rast të vetëm.
- Fshehja e rreshtave. Rreshta të veçantë mund të shënohen si Mos e printo (në set: I fshehur). Ata hyjnë në total, por nuk printohen dhe nuk shfaqen në portalin e klientit. Shenja kalon nga oferta te fatura.
- Çmim fiks për çdo rresht seti. Një rresht seti mund të mbajë një çmim njësie fiks që zëvendëson çmimin nga lista e çmimeve, për shembull për një korrigjim çmimi të fshehur.
Pa modulin (vetëm sale_product_set) | Me sale_product_set_layout |
|---|---|
| Rreshtat e setit si pozicione të shkëputura | Rresht seksioni me emrin e setit, sasia në emër, rreshtat poshtë |
| Seksionet në set mbeten seksione dhe e copëtojnë strukturën | Seksionet në set bëhen nënseksione (line_subsection) |
| Pamja e printuar gjithmonë e njëjtë | Mënyra e printimit për çdo set, e ndryshueshme gjatë futjes |
| Korrigjimi i çmimit i dukshëm në ofertë dhe në faturë | Rreshti mund të fshihet, por hyn në total |
| Çmimi vetëm nga lista e çmimeve | Opsionalisht çmim fiks për çdo rresht seti |
| Struktura përfundon te oferta | Struktura dhe fshehja vlejnë për ofertën, porosinë, faturën dhe portalin e klientit |
Tri mënyrat e printimit në detaje
Mënyra e printimit është një fushë zgjedhjeje print_mode te seti i produkteve. Gjatë futjes ajo përkthehet në dy çelësat standardë të rreshtit të seksionit. Për këtë në modul ekziston një përshoqërim i vetëm, lehtësisht i lexueshëm:
# Druckmodus -> (collapse_prices, collapse_composition) der Abschnittszeile
PRINT_MODE_FLAGS = {
"detailed": (False, False),
"total_only": (True, False),
"summary": (False, True),
}
Mënyra (print_mode) | Shfaqja në set | Opsioni standard në Odoo | Pamja e printuar |
|---|---|---|---|
detailed | Çdo rresht me çmim | asnjë | Seksion, të gjithë rreshtat me shumë |
total_only | Rreshta pa çmime, vetëm shuma e setit | collapse_prices (Fshih çmimet) | Seksion me shumën, rreshtat vetëm me emërtim dhe sasi |
summary | Vetëm një rresht me shumën e setit | collapse_composition (Fshih përbërjen) | Vetëm rreshti i seksionit me shumën |
Meqenëse moduli përdor opsionet standarde të Odoo 19 dhe jo një format të vetin printimi, oferta, konfirmimi i porosisë, fatura dhe portali i klientit funksionojnë pa shabllone raportesh të përshtatura. Kush ka shabllone raportesh të veta që marrin parasysh opsionet e seksionit, e merr po ashtu këtë sjellje.
Rreshta të fshehur dhe çmim fiks
Një rast i shpeshtë: një paketë ka një çmim paketë që është nën shumën e çmimeve të veçanta. Në vend që të shtrembërohen çmimet e veçanta, përdoret një rresht korrigjimi me çmim fiks (për shembull −50,00 € për set) dhe fshihet. Në backend fatura është e gjurmueshme, në printim shfaqet çmimi i paketës.
E rëndësishme për të kuptuar: shenja hide_in_report nuk ndryshon asgjë në llogaritje. Sasia, çmimi, zbritja, tatimi, shumat dhe regjistrimet kontabël krijohen të pandryshuara nga Odoo standard. Rreshti mungon vetëm në printim dhe në portal.
- Te seti i produkteve: rresht seti me I fshehur dhe opsionalisht Çmim fiks së bashku me Çmim njësie.
- Te rreshtat e ofertës dhe të faturës: kolona Mos e printo (
hide_in_report), edhe për rreshta normalë pa set. - Transferimi: Gjatë krijimit të faturës nga porosia, shenja kalon në rreshtin e faturës. Gjatë kopjimit të një oferte ajo ruhet.
Zbatimi teknik
Moduli është qëllimisht i vogël: zgjeron modelet ekzistuese dhe mbishkruan vetëm vendet ku Odoo vendos cilët rreshta printohen dhe si futet një set. Nuk ka shabllone të rinj raportesh, modele të reja dhe ndërhyrje SQL.
Modelet dhe fushat e zgjeruara
| Modeli | Shtesa |
|---|---|
product.set | Fushat insert_as_section (Boolean, e aktivizuar si parazgjedhje) dhe print_mode (zgjedhje, fushë e detyrueshme, parazgjedhje detailed) |
product.set.line | Fushat hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() i kalon ato te rreshti i ofertës |
sale.product.set.wizard | Fushat insert_as_section, section_name, print_mode; _prepare_order_lines() ndërton rreshtin e seksionit |
sale.order / sale.order.line | Fusha hide_in_report (me copy=True); _get_order_lines_to_report() kalon rreshtat e fshehur; _prepare_invoice_line() e kalon shenjën më tej |
account.move / account.move.line | Fusha hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() dhe _get_subsection_lines() filtrojnë rreshtat e fshehur; _get_aml_vals() e vendos shenjën në të dhënat e raportit |
Fshehja në ofertë
Odoo 19 përcakton rreshtat e ofertës që do të printohen te _get_order_lines_to_report(). Moduli aty shton një filtër. Ndërhyrja në shabllonin e raportit nuk është e nevojshme: si shablloni i raportit të ofertës (sale/report/ir_actions_report_templates.xml) ashtu edhe shablloni i portalit (sale/views/sale_portal_templates.xml) e thërrasin këtë metodë:
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
)
Kalimi te fatura
Gjatë krijimit të rreshtit të faturës nga rreshti i ofertës (_prepare_invoice_line()) shenja kopjohet. Në anën e faturës, në Odoo 19 ka disa rrugë nëpër të cilat rreshtat arrijnë në raport: lista kryesore, rreshtat e një seksioni dhe rreshtat e një nënseksioni. Prandaj moduli filtron në disa vende (_get_move_lines_to_report() te dokumenti, _get_detailed_lines() dhe _get_subsection_lines() te rreshti), përndryshe një rresht i fshehur mund të shfaqej sërish përmes një prej rrugëve të tjera:
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")]
Asistenti: seksion në vend të rreshtave të shkëputur
Asistenti sale.product.set.wizard nga sale_product_set krijon rreshtat e ofertës te _prepare_order_lines(). Moduli e mbishkruan këtë metodë. Nëse Fute si seksion është e çaktivizuar, ai thjesht thërret metodën origjinale, sjellja mbetet atëherë si në modulin OCA. Përndryshe krijohet fillimisht rreshti i seksionit me dy çelësat standardë, pastaj pasojnë rreshtat e setit. Seksionet brenda setit bëhen nënseksione, që të mos e copëtojnë strukturën kryesore:
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
Vendime që nuk shihen
- Metoda llogaritjeje të ndara në asistent. Emri i seksionit, mënyra e printimit dhe Fute si seksion kanë secili metodën e vet
compute(store=True,readonly=False). Nëse të tre do të kishin të njëjtën metodë, një vlerë e dhënë shprehimisht për një fushë do t'i rivendoste të tjerat. Kjo vihet re sidomos kur asistenti krijohet me vlera nga kodi ose nga testet. - Emri i seksionit me sasinë. Kur sasia është e ndryshme nga 1, ajo shfaqet në emër (2 × Messkit Standard, me presje si ndarës dhjetor), që në printim të kuptohet se janë dy sete dhe jo një set me sasi të dyfishtë.
- Çmimi fiks vetëm te rreshtat e produkteve.
prepare_sale_order_line_values()vendoshide_in_reportdheprice_unitvetëm nëse rreshti i setit nuk është rresht seksioni ose shënimi. - Odoo standard në vend të zgjidhjes së vetë. Mënyra e printimit dhe fshehja përdorin grepa dhe fusha ekzistuese të Odoo 19. Kjo i bën përditësimet të kontrollueshme: nëse Odoo ndryshon shabllonin e raportit, moduli mbetet i vlefshëm për sa kohë ekzistojnë metodat.
Instalim pa efekte anësore të padëshiruara
Kushtet paraprake: Odoo 19.0, sale_product_set (OCA/sale-workflow, branch 19.0) dhe product_set (OCA/product-attribute, branch 19.0). Vetë moduli varet nga sale_management, sale_product_set dhe account.
Kurthi auto_install te modulet OCA. Shumë repozitorë OCA përmbajnë module me auto_install. Sapo të ekzistojnë varësitë e tyre, Odoo i instalon ato bashkë pa pyetur gjatë instalimit ose përditësimit të radhës. Kush prandaj vendos repozitorin e plotë OCA në addons_path të një instance produktive, mund të marrë module që askush nuk i ka dashur. Te OCA/product-attribute kjo vlen për shembull për purchase_product_template_tags. Mënyra jonë e veprimit:
- Klononi sipërfaqësisht dy repozitorët OCA (
git clone --depth 1 --branch 19.0). - Në një dosje të veçantë lidhni me symlink vetëm dy modulet e nevojshme dhe shtoni këtë dosje në
addons_path, jo repozitorët e plotë. - Instaloni në një bazë të dhënash testimi dhe kontrolloni cilat module marrin gjendjen të instaluar.
- Vetëm pastaj instaloni në produksion, me
--workers=0, që thirrja të mos konkurrojë me shërbimin në punë për portën HTTP.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init
Fushat dhe pamjet e reja janë në bazën e të dhënave pas instalimit. Nëse shërbimi punon në mënyrë shumëprocesore, workers e rimbarkojnë regjistrin e ndryshuar përmes sinjalit të bazës së të dhënave. Ndryshimet në skedarët Python të modulit hyjnë në fuqi vetëm pas një rinisjeje të shërbimit.
Përdorimi hap pas hapi
- Krijimi i setit: Shitje → Produkte → Sete produktesh. Emërtoni setin, zgjidhni Fute si seksion dhe Mënyra e printimit, shënoni produktet me sasi. Rreshtat e korrigjimit pajisini me I fshehur dhe Çmim fiks.
- Në ofertë: Klikoni Shto set, zgjidhni setin dhe sasinë. Emri i seksionit dhe mënyra e printimit mund të ndryshohen para futjes.
- Fshehja e rreshtave të veçantë: Në ofertë ose në faturë aktivizoni kolonën Mos e printo.
- Kontrolli: Hapni pamjen paraprake ose PDF-në. Të njëjtën paraqitje e shohin klientët në portal.
Testet
Moduli vjen me gjashtë teste të automatizuara, që i ekzekutojmë mbi një bazë të dhënash testimi para se diçka të shkojë në produksion:
- Futja si seksion përfshirë mënyrën e printimit dhe nënseksionin
- Rreshti i fshehur mungon në rreshtat që do të printohen
- Mënyra Një rresht (
summary) - Futja pa seksion, sjellja origjinale
- Fatura merr shenjën
hide_in_report, rreshti mungon te_get_move_lines_to_report(), shuma e seksionit e përmban atë përsëri - Fatura në mënyrën Vetëm çmimi total nuk e shfaq rreshtin e fshehur në rreshtat e seksionit, shuma e seksionit mbetet e plotë
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
--test-enable --test-tags=/sale_product_set_layout --stop-after-init
Përkthimi gjerman ndodhet te i18n/de.po.
Kufizimet dhe shënim ligjor
- Vetëm struktura. Moduli nuk ndryshon mënyrën se si Odoo llogarit çmimet, zbritjet ose tatimet. Ndryshon vetëm atë që printohet dhe shfaqet në portal.
- Rreshtat e fshehur janë të dukshëm në backend. Shenja prek printimin dhe portalin, jo pamjen në ekran të punonjësve.
- Kontrolloni të dhënat e faturës. Rreshtat e fshehur hyjnë në total, por nuk shfaqen në dokument. Nëse kjo përputhet me kërkesat ndaj faturave në vendin tuaj, për shembull me përshkrimin e detyrueshëm të sendeve të dorëzuara ose të shërbimeve të kryera, duhet ta sqaroni para përdorimit në fatura me këshilltarin tuaj tatimor. Te ofertat fshehja është më pak problematike. Shkrimi nuk është këshillë ligjore.
- Testuar për Odoo 19. E kemi zhvilluar dhe testuar modulin për Odoo 19.0 Community. Ai përdor opsione seksionesh që ekzistojnë në këtë formë në Odoo 19. Për versione të tjera nuk e kemi kontrolluar.
Pyetje të shpeshta
Pse më duhet moduli, nëse Odoo 19 i di tashmë seksionet?
Opsionet e seksioneve ekzistojnë në vetë Odoo. Mungonte lidhja me setin e produkteve: një set duhet të futej automatikisht si seksion, me mënyrën e printimit të ruajtur në set. Pa modul, pas çdo futjeje duhet krijuar dhe konfiguruar me dorë rreshti i seksionit.
Si ndryshojnë „Fshih çmimet“ dhe „Fshih përbërjen“?
„Fshih çmimet“ (collapse_prices) i lë rreshtat, por nuk shfaq shuma, vetëm shumën te seksioni. „Fshih përbërjen“ (collapse_composition) i fsheh rreshtat plotësisht, klienti sheh vetëm seksionin me shumën.
A hyn një rresht i fshehur në shumën e faturës?
Po. Rreshtat e fshehur mbeten në shumë, tatim dhe regjistrim. Shenja prek vetëm paraqitjen në printim dhe në portalin e klientit.
A mund ta ndryshoj mënyrën e printimit gjatë futjes?
Po. Mënyra vjen si parazgjedhje nga seti, në asistent mund të ndryshohet për futjen përkatëse. Vetë seti mbetet i pandryshuar.
Çfarë ndodh nëse e çaktivizoj Fute si seksion?
Atëherë asistenti sillet si në modulin OCA sale_product_set: rreshtat e setit futen pa seksion.
A instalon moduli papritur module të tjera?
Vetë moduli jo. Por te repozitorët OCA mund të vijë deri te modulet auto_install, nëse vendosen repozitorë të tërë në addons_path. Prandaj lidhni me symlink vetëm dy modulet e nevojshme (shihni seksionin Instalimi).
A punon moduli me Odoo Enterprise?
Moduli varet vetëm nga module Community (sale_management, account) dhe nga dy modulet OCA. Ne e përdorim në Odoo 19 Community. Në Enterprise nuk e kemi testuar.
Nën cilën licencë është moduli?
Nën AGPL-3. Mund ta përdorni, ta përshtatni dhe ta shpërndani, për sa kohë ndryshimet mbeten nën të njëjtën licencë. Gabimet dhe sugjerimet për përmirësim janë të mirëpritura si issue në repozitorin GitHub .
Mbështetje nga Biricon IT Services
Ne mirëmbajmë instalime Odoo për ndërmarrje të vogla dhe të mesme: nga futja në përdorim, përshtatjet dhe migrimet deri te funksionimi i vazhdueshëm. Nëse dëshironi t'i përshtatni setet e produkteve, shabllonet e ofertave ose raportet në Odoo 19 me proceset tuaja, ose nëse dëshironi ta kontrolloni një modul si ky dhe ta sillni në mënyrë të sigurt në instancën tuaj produktive, na vjen mirë t'ju mbështesim. Zhvillime të tjera të brendshme i gjeni në blogun tonë, për shembull Bashkimi i faturave draft dhe Lidhja automatike e pagesave bankare me faturat.
Gjendja: 2 nëntor 2026. Versioni i modulit 19.0.1.0.0. Të dhënat e shembullit në pamje janë të zgjedhura lirisht, shumat neto.