Skip to Content

Odoo 19 Community: produktų rinkiniai pasiūlyme kaip skyrius – mūsų atvirojo kodo modulis Sale Product Set Layout

2026 m. lapkričio 2 d. | Odoo 19 Community | Nuosava plėtra
2026 m. lapkričio 2 d. pagal
Odoo 19 Community: produktų rinkiniai pasiūlyme kaip skyrius – mūsų atvirojo kodo modulis Sale Product Set Layout

Odoo 19 modulis Sale Product Set Layout: produktų rinkiniai kaip pasiūlymo skyrius su spausdinimo režimu, paslėptomis eilutėmis ir fiksuota kaina

Kas Odoo parduoda paketus, pavyzdžiui, matavimo prietaisą su priedais ir montavimu, darbo vietą su įranga ir paruošimu ar techninės priežiūros paketą iš kelių paslaugų, pasiūlyme vienu metu reikia dviejų dalykų: sudėtis turi būti nurodyta užsakyme, kad sandėlis, technikai ir sąskaitų rengimas žinotų, kas bus pristatoma. Tačiau kliento spausdinamame dokumente ne visada turi būti rodoma kiekviena pozicija su kiekviena kaina.

OCA modulis sale_product_set leidžia vienu paspaudimu įterpti į pasiūlymą produktų rinkinius (pardavimo komplektacijas). Dėl spausdinimo vaizdo jis nenusprendžia. Todėl sukūrėme modulį Sale Product Set Layout (sale_product_set_layout). Jis parašytas Odoo 19 ir kaip laisvoji programinė įranga pagal AGPL-3 licenciją nemokamai prieinamas GitHub:

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

Problema: produktų rinkiniai be įtakos spausdinimo vaizdui

Odoo produktų rinkinys yra paruoštas produktų sąrašas su kiekiais. Užuot ieškojus dešimties prekių po vieną, pasiūlyme pasirenkama Pridėti rinkinį ir iš karto gaunamos visos eilutės. Tai taupo laiką ir apsaugo nuo pamirštų pozicijų. Modulis sale_product_set iš saugyklos OCA/sale-workflow daro būtent tai; patys rinkiniai yra iš product_set saugyklos OCA/product-attribute.

Praktikoje lieka trys spragos:

  • Nėra matomo grupavimo. Rinkinio eilutės pasiūlyme atsiduria kaip pavienės pozicijos. Po įterpimo nebematyti, kurios eilutės priklauso viena kitai, o bendros rinkinio sumos niekur nėra.
  • Jokios įtakos spausdinamam dokumentui. Kartais klientas turi matyti kiekvieną poziciją su kaina, kartais – tik paketo kainą, kartais – tik vieną eilutę. Tai sprendimas pagal rinkinį ar klientą, o ne kiekvieną kartą rankomis keičiamas kiekviename pasiūlyme.
  • Kainų korekcijos atrodo nemaloniai. Jei paketas turi fiksuotą kainą, besiskiriančią nuo kainoraščio kainų sumos, reikia korekcijos eilutės. Ji priklauso skaičiavimui, bet nebūtinai kliento popieriui.

Odoo 19 turi sudedamųjų dalių, leidžiančių tai išspręsti tvarkingai: pasiūlymo eilutėse yra skyriai ir poskyriai su parinktimis Slėpti kainas ir Slėpti sudėtį (laukai collapse_prices ir collapse_composition). Modulis susieja produktų rinkinį su šiomis standartinėmis parinktimis, užuot kūręs savo spausdinimo formatą.

Ką modulis daro

  1. Rinkinio įterpimas kaip skyriaus. Vedlys Pridėti rinkinį sukuria skyriaus eilutę su rinkinio pavadinimu, pavyzdžiui, 2 × Messkit Standard, ir po ja įterpia rinkinio eilutes. Rinkinio viduje esantys skyriai tampa poskyriais.
  2. Spausdinimo režimas kiekvienam rinkiniui. Rinkinyje vieną kartą nustatoma, kaip jis spausdinamas. Įterpiant režimą galima pakeisti vedlyje tik tam vienam atvejui.
  3. Eilučių slėpimas. Atskiras eilutes galima pažymėti kaip Nespausdinti (rinkinyje: Paslėpta). Jos įskaičiuojamos į sumą, bet nei spausdinamos, nei rodomos klientų portale. Žymė iš pasiūlymo perkeliama į sąskaitą faktūrą.
  4. Fiksuota kaina kiekvienai rinkinio eilutei. Rinkinio eilutė gali turėti fiksuotą vieneto kainą, kuri pakeičia kainoraščio kainą, pavyzdžiui, paslėptai kainos korekcijai.
Be modulio (tik sale_product_set)Su sale_product_set_layout
Rinkinio eilutės kaip pavienės pozicijosSkyriaus eilutė su rinkinio pavadinimu, kiekis pavadinime, eilutės po ja
Rinkinio skyriai lieka skyriais ir suardo struktūrąRinkinio skyriai tampa poskyriais (line_subsection)
Spausdinimo vaizdas visada tas patsSpausdinimo režimas kiekvienam rinkiniui, įterpiant keičiamas
Kainos korekcija matoma pasiūlyme ir sąskaitojeEilutę galima paslėpti, bet ji įskaičiuojama į sumą
Kaina tik iš kainoraščioPasirinktinai fiksuota kaina kiekvienai rinkinio eilutei
Maketas baigiasi pasiūlymeMaketas ir slėpimas galioja pasiūlymui, užsakymui, sąskaitai faktūrai ir klientų portalui

Trys spausdinimo režimai išsamiai

Spausdinimo režimas yra pasirinkimo laukas print_mode prie produktų rinkinio. Įterpiant jis paverčiamas dviem standartiniais skyriaus eilutės jungikliais. Tam modulyje yra vienas lengvai skaitomas priskyrimas:

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

Trys to paties produktų rinkinio spausdinimo vaizdai: kiekviena eilutė su kaina, tik bendra kaina, viena eilutė

Režimas (print_mode)Rodinys rinkinyjeStandartinė parinktis OdooSpausdinimo vaizdas
detailedKiekviena eilutė su kainanėraSkyrius, visos eilutės su sumomis
total_onlyEilutės be kainų, tik rinkinio sumacollapse_prices (Slėpti kainas)Skyrius su suma, eilutės tik su pavadinimu ir kiekiu
summaryTik viena eilutė su rinkinio sumacollapse_composition (Slėpti sudėtį)Tik skyriaus eilutė su suma

Kadangi modulis naudoja standartines Odoo 19 parinktis, o ne savo spausdinimo formatą, pasiūlymas, užsakymo patvirtinimas, sąskaita faktūra ir klientų portalas veikia be pritaikytų ataskaitų šablonų. Kas turi savo ataskaitų šablonus, atsižvelgiančius į skyrių parinktis, tą patį elgesį gauna taip pat.

Paslėptos eilutės ir fiksuota kaina

Dažnas atvejis: paketo kaina yra mažesnė už atskirų kainų sumą. Užuot iškraipius atskiras kainas, naudojama korekcijos eilutė su fiksuota kaina (pavyzdžiui, −50,00 € už rinkinį) ir ji paslepiama. Vidinėje sistemoje sąskaita lengvai atsekama, o spausdintame dokumente matoma paketo kaina.

Kainos korekcija vidinėje sistemoje matoma ir pažymėta, pasiūlymo, PDF, sąskaitos ir portalo spausdintame dokumente nematoma, suma ta pati

Svarbu suprasti: žymė hide_in_report nieko nekeičia skaičiavime. Kiekis, kaina, nuolaida, mokestis, sumos ir buhalterijos įrašai lieka tokie, kokius sukuria standartinė Odoo. Eilutės trūksta tik spausdintame dokumente ir portale.

  • Produktų rinkinyje: rinkinio eilutė su Paslėpta ir pasirinktinai Fiksuota kaina kartu su Vieneto kaina.
  • Pasiūlymo ir sąskaitos eilutėse: stulpelis Nespausdinti (hide_in_report), taip pat paprastoms eilutėms be rinkinio.
  • Perdavimas: kuriant sąskaitą iš užsakymo, žymė perkeliama į sąskaitos eilutę. Kopijuojant pasiūlymą, ji išlieka.

Techninis įgyvendinimas

Modulis sąmoningai mažas: jis išplečia esamus modelius ir perrašo tik tas vietas, kuriose Odoo sprendžia, kurios eilutės spausdinamos ir kaip įterpiamas rinkinys. Nėra naujų ataskaitų šablonų, naujų modelių ir SQL intervencijų.

Duomenų srautas: produktų rinkinys, vedlys Pridėti rinkinį, pasiūlymo eilutės, sąskaitos eilutės, PDF ir klientų portalas

Išplėsti modeliai ir laukai

ModelisPapildymas
product.setLaukai insert_as_section (Boolean, pagal numatytuosius nustatymus įjungtas) ir print_mode (pasirinkimas, privalomas laukas, numatytasis detailed)
product.set.lineLaukai hide_in_report, fixed_price, price_unit; prepare_sale_order_line_values() perduoda juos pasiūlymo eilutei
sale.product.set.wizardLaukai insert_as_section, section_name, print_mode; _prepare_order_lines() sukuria skyriaus eilutę
sale.order / sale.order.lineLaukas hide_in_report (su copy=True); _get_order_lines_to_report() praleidžia paslėptas eilutes; _prepare_invoice_line() perduoda žymę toliau
account.move / account.move.lineLaukas hide_in_report; _get_move_lines_to_report(), _get_detailed_lines() ir _get_subsection_lines() filtruoja paslėptas eilutes; _get_aml_vals() perkelia žymę į ataskaitos duomenis

Slėpimas pasiūlyme

Odoo 19 spausdintinas pasiūlymo eilutes nustato _get_order_lines_to_report(). Modulis ten prideda filtrą. Ingerencija į ataskaitos šabloną nereikalinga: ir pasiūlymo ataskaitos šablonas (sale/report/ir_actions_report_templates.xml), ir portalo šablonas (sale/views/sale_portal_templates.xml) šį metodą iškviečia:

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
        )

Perdavimas į sąskaitą

Kuriant sąskaitos eilutę iš pasiūlymo eilutės (_prepare_invoice_line()), žymė nukopijuojama. Sąskaitos pusėje Odoo 19 yra keli keliai, kuriais eilutės patenka į ataskaitą: pagrindinis sąrašas, skyriaus eilutės ir poskyrio eilutės. Todėl modulis filtruoja keliose vietose (_get_move_lines_to_report() prie dokumento, _get_detailed_lines() ir _get_subsection_lines() prie eilutės), kitaip paslėpta eilutė galėtų vėl atsirasti vienu iš kitų kelių:

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

Vedlys: skyrius vietoj pavienių eilučių

Vedlys sale.product.set.wizard iš sale_product_set pasiūlymo eilutes sukuria metode _prepare_order_lines(). Modulis šį metodą perrašo. Jei Įterpti kaip skyrių išjungta, jis tiesiog iškviečia pradinį metodą ir elgesys lieka toks pat kaip OCA modulyje. Priešingu atveju pirmiausia sukuriama skyriaus eilutė su dviem standartiniais jungikliais, po to eina rinkinio eilutės. Rinkinio viduje esantys skyriai tampa poskyriais, kad nesuardytų pagrindinės struktūros:

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

Sprendimai, kurių nematyti

  • Atskiri skaičiavimo metodai vedlyje. Skyriaus pavadinimas, spausdinimo režimas ir Įterpti kaip skyrių turi po atskirą compute metodą (store=True, readonly=False). Jei visi trys naudotų tą patį metodą, aiškiai perduota vieno lauko reikšmė atstatytų kitus. Tai ypač pastebima, kai vedlys kuriamas iš kodo ar testų su reikšmėmis.
  • Skyriaus pavadinimas su kiekiu. Kai kiekis nelygus 1, jis nurodomas pavadinime (2 × Messkit Standard, kableliu kaip dešimtainiu skyrikliu), kad spausdintame dokumente būtų aišku, jog tai du rinkiniai, o ne vienas rinkinys dvigubo kiekio.
  • Fiksuota kaina tik produktų eilutėms. prepare_sale_order_line_values() nustato hide_in_report ir price_unit tik tada, kai rinkinio eilutė nėra skyriaus ar pastabos eilutė.
  • Standartinė Odoo vietoj savadarbio sprendimo. Spausdinimo režimas ir slėpimas naudoja esamus Odoo 19 kabliukus ir laukus. Dėl to atnaujinimai valdomi: jei Odoo pakeičia ataskaitos šabloną, modulis lieka galiojantis, kol metodai egzistuoja.

Diegimas be nepageidaujamo šalutinio poveikio

Reikalavimai: Odoo 19.0, sale_product_set (OCA/sale-workflow, šaka 19.0) ir product_set (OCA/product-attribute, šaka 19.0). Pats modulis priklauso nuo sale_management, sale_product_set ir account.

auto_install spąstai OCA moduliuose. Daugelyje OCA saugyklų yra modulių su auto_install. Kai tik atsiranda jų priklausomybės, Odoo juos be klausimo įdiegia kitą kartą diegiant ar atnaujinant. Todėl, kas visą OCA saugyklą deda į produkcinės sistemos addons_path, gali gauti modulių, kurių niekas nenorėjo. Saugykloje OCA/product-attribute tai, pavyzdžiui, liečia purchase_product_template_tags. Mūsų veiksmai:

  1. Abi OCA saugyklas klonuoti paviršutiniškai (git clone --depth 1 --branch 19.0).
  2. Atskirame kataloge simboline nuoroda susieti tik du reikalingus modulius ir šį katalogą įtraukti į addons_path, o ne visas saugyklas.
  3. Įdiegti bandomojoje duomenų bazėje ir patikrinti, kuriems moduliams tuo metu suteikiama būsena įdiegta.
  4. Tik tada diegti produkcinėje sistemoje su --workers=0, kad komanda nekonkuruotų su veikiančia tarnyba dėl HTTP prievado.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init

Nauji laukai ir rodiniai po įdiegimo yra duomenų bazėje. Jei tarnyba veikia daugiaprocesiu režimu, darbiniai procesai pakeistą registrą įkelia per duomenų bazės signalą. Modulio Python failų pakeitimai įsigalioja tik paleidus tarnybą iš naujo.

Naudojimas žingsnis po žingsnio

  1. Rinkinio sukūrimas: Pardavimai → Produktai → Produktų rinkiniai. Pavadinti rinkinį, pasirinkti Įterpti kaip skyrių ir Spausdinimo režimą, įvesti produktus su kiekiais. Korekcijos eilutėms nustatyti Paslėpta ir Fiksuota kaina.
  2. Pasiūlyme: spustelėti Pridėti rinkinį, pasirinkti rinkinį ir kiekį. Skyriaus pavadinimą ir spausdinimo režimą prieš įterpiant galima pakeisti.
  3. Atskirų eilučių slėpimas: pasiūlyme ar sąskaitoje įjungti stulpelį Nespausdinti.
  4. Patikrinimas: atidaryti peržiūrą arba PDF. Tą patį vaizdą klientai mato portale.

Testai

Modulis turi šešis automatizuotus testus, kuriuos vykdome bandomojoje duomenų bazėje prieš pradedant naudoti produkcinėje sistemoje:

  • Įterpimas kaip skyriaus, įskaitant spausdinimo režimą ir poskyrį
  • Paslėptos eilutės nėra tarp spausdintinų eilučių
  • Režimas Viena eilutė (summary)
  • Įterpimas be skyriaus, pradinis elgesys
  • Sąskaita perima žymę hide_in_report, eilutės nėra _get_move_lines_to_report(), skyriaus suma ją vis tiek apima
  • Sąskaita režimu Tik bendra kaina neparodo paslėptos eilutės skyriaus eilutėse, skyriaus suma lieka išsami
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
         --test-enable --test-tags=/sale_product_set_layout --stop-after-init

Vertimas į vokiečių kalbą yra faile i18n/de.po.

Apribojimai ir teisinė pastaba

  • Tik maketas. Modulis nekeičia, kaip Odoo skaičiuoja kainas, nuolaidas ar mokesčius. Jis keičia tik tai, kas spausdinama ir rodoma portale.
  • Paslėptos eilutės vidinėje sistemoje matomos. Žymė liečia spausdintą dokumentą ir portalą, o ne darbuotojų ekrano vaizdą.
  • Patikrinkite sąskaitų duomenis. Paslėptos eilutės įskaičiuojamos į sumą, bet dokumente jų nėra. Ar tai suderinama su jūsų šalies reikalavimais sąskaitoms, pavyzdžiui, su privalomu pristatytų daiktų ar suteiktų paslaugų aprašymu, turėtumėte išsiaiškinti su savo mokesčių konsultantu prieš naudodami sąskaitose. Pasiūlymuose slėpimas yra mažiau problemiškas. Šis straipsnis nėra teisinė konsultacija.
  • Išbandyta su Odoo 19. Modulį sukūrėme ir išbandėme su Odoo 19.0 Community. Jis naudoja skyrių parinktis, kurios tokia forma yra Odoo 19. Kitų versijų netikrinome.

Dažniausiai užduodami klausimai

Kam man reikalingas modulis, jei Odoo 19 jau moka skyrius?

Skyrių parinktys yra pačioje Odoo. Trūko ryšio su produktų rinkiniu: rinkinys turėjo būti automatiškai įterpiamas kaip skyrius su rinkinyje nustatytu spausdinimo režimu. Be modulio po kiekvieno įterpimo skyriaus eilutę reikia sukurti ir sukonfigūruoti rankomis.

Kuo skiriasi „Slėpti kainas“ ir „Slėpti sudėtį“?

„Slėpti kainas“ (collapse_prices) palieka eilutes, bet nerodo sumų, tik skyriaus sumą. „Slėpti sudėtį“ (collapse_composition) eilutes paslepia visiškai, klientas mato tik skyrių su suma.

Ar paslėpta eilutė įskaičiuojama į sąskaitos sumą?

Taip. Paslėptos eilutės lieka sumoje, mokestyje ir buhalterinėje apskaitoje. Žymė liečia tik vaizdavimą spausdintame dokumente ir klientų portale.

Ar įterpiant dar galiu pakeisti spausdinimo režimą?

Taip. Režimas paimamas kaip numatytasis iš rinkinio, o vedlyje jį galima pakeisti konkrečiam įterpimui. Pats rinkinys tuo metu nesikeičia.

Kas atsitinka, jei išjungiu Įterpti kaip skyrių?

Tada vedlys elgiasi kaip OCA modulyje sale_product_set: rinkinio eilutės įterpiamos be skyriaus.

Ar modulis netikėtai neįdiegia kitų modulių?

Pats modulis – ne. Tačiau OCA saugyklose gali pasitaikyti auto_install modulių, jei į addons_path dedamos ištisos saugyklos. Todėl susiekite tik du reikalingus modulius (žr. skyrių Diegimas).

Ar modulis veikia su Odoo Enterprise?

Modulis priklauso tik nuo Community modulių (sale_management, account) ir dviejų OCA modulių. Mes jį naudojame su Odoo 19 Community. Su Enterprise neišbandėme.

Pagal kokią licenciją platinamas modulis?

Pagal AGPL-3. Galite jį naudoti, pritaikyti ir platinti, kol pakeitimai lieka pagal tą pačią licenciją. Apie klaidas ir tobulinimo pasiūlymus laukiame pranešimų užduočių (issue) pavidalu GitHub saugykloje .

Biricon IT Services palaikymas

Prižiūrime Odoo diegimus mažoms ir vidutinėms įmonėms: nuo įdiegimo, pritaikymų ir migracijų iki nuolatinio veikimo. Jei norite pritaikyti produktų rinkinius, pasiūlymų šablonus ar ataskaitas Odoo 19 savo procesams arba norite peržiūrėti tokį modulį ir saugiai perkelti jį į savo produkcinę sistemą, mielai padėsime. Kitų nuosavų kūrinių rasite mūsų tinklaraštyje, pavyzdžiui, Sąskaitų projektų sujungimas ir Banko mokėjimų automatinis priskyrimas sąskaitoms.

Susisiekite dabar

Būklė: 2026 m. lapkričio 2 d. Modulio versija 19.0.1.0.0. Paveikslų pavyzdiniai duomenys parinkti laisvai, sumos be PVM.