Preskoči na vsebino

Odoo 19 Community: združevanje osnutkov računov – naš odprtokodni modul Merge Draft Invoices

18. september 2026 napisal
Odoo 19 Community: združevanje osnutkov računov – naš odprtokodni modul Merge Draft Invoices
Alexander Biringer | Biricon IT Services GmbH

Kdor v Odoo obračunava iz naročil, zahtevkov helpdeska in beleženja časa, pozna rezultat: stranka ob koncu meseca ne dobi enega računa, temveč pet. Vsak izvira iz drugega naročila ali zahtevka, vsak je strokovno pravilen – in vendar stranka vpraša, ali ne bi vse šlo na en dokument.

Za to v Odoo 19 Community ni čiste rešitve. Znani modul OCA account_invoice_merge za Odoo 19 še ni odobren in problem rešuje na način, ki za ponudnike storitev pomeni resno tveganje: enake postavke se združijo in s tem se izgubi povezava nazaj do naročila, naloge in zabeleženega časa.

Za lastno namestitev Odoo 19 Community smo zato razvili modul Merge Draft Invoices. Ker ta težava ne zadeva le nas, ga brezplačno ponujamo na GitHubu pod prosto licenco AGPL-3.

Na projekt na GitHubu:

Odoo 19 Merge Draft Invoices

Zakaj lasten modul namesto rešitve OCA?

OCA že vrsto let vzdržuje account_invoice_merge v repozitoriju OCA/account-invoicing. Ob času tega prispevka je migracija na 19.0 tam še odprt pull request, ne objavljen modul.

Pomembnejše pa je, da modula počneta različni stvari:


OCA account_invoice_mergeMerge Draft Invoices
Rezultatse združi v enega od izbranih računovvedno nov račun
Postavkeenake postavke se združijo, količine se seštejovsaka postavka se prevzame ena proti ena
Izvorinvoice_origin / payment_reference v glavi se zlepitapolja izvora na postavko se kopirajo prek Odoojeve lastne kljuke _copy_data_extend_business_fields
Beleženje časani upoštevanotimesheet_invoice_id se prenese na nov račun
Berljivo za strankoničnaslov nad postavkami vsakega vira z naročilom in zahtevkom
Izvorni računise stornirajostornirati (privzeto), izbrisati ali obdržati – z jasno poimenovanimi posledicami

Združevanje enakih postavk je odločilna razlika. Združena postavka lahko nosi le en izvor. Takoj ko dve vrstici iz dveh različnih naročil izgledata enako, je povezava od računa nazaj do naročila, naloge in knjižbe časa izgubljena. V storitvenem podjetju, kjer obračun poteka prek zahtevek → beleženje časa → račun, to pomeni: iste ure se zaračunajo dvakrat ali izginejo.

Naš modul zato načeloma ničesar ne združuje. Združeni račun ima natanko toliko postavk kot njegovi viri skupaj.

Iz modula OCA ni bila prevzeta nobena koda. Oba modula sta pod AGPL-3.

Kaj modul počne

V seznamskem pogledu računov označite dva ali več osnutkov, v meniju dejanj izberite Združi osnutke računov – pomočnik prikaže izbor, preverjanja in možnosti za izvorne račune. Po potrditvi pristanete neposredno na novem računu.

Pred združitvijo modul preveri, in sicer vse točke naenkrat, da po vsakem popravku ne vidite naslednje napake:

  • izbrana sta vsaj dva računa
  • vsi so v stanju osnutek
  • noben ni bil nikoli knjižen (dokument, vrnjen v osnutek, ohrani zgodovino knjiženja in se zavrne)
  • noben ni bil že združen
  • vsi so račun, račun dobavitelja, dobropis ali dokument – ne zgolj knjižbe
  • isti partner, vrsta dokumenta, podjetje, valuta in dnevnik

Razlike pri plačilnem pogoju, davčni dodelitvi, naslovu dobave, bančnem računu ali prodajalcu ne blokirajo. Pomočnik opozori in prevzame vrednost najstarejšega izbranega računa.

Kaj ostane ohranjeno

Bistvo modula je, da vsaka povezava, ki jo postavka računa ima s svojim izvorom, obstaja tudi na novem računu:

PovezavaObravnava
Postavke prodajnega naloga (sale_line_ids)se kopirajo na postavko prek Odoojevega _copy_data_extend_business_fields
Postavke nabavnega naročila (purchase_line_id)enako
Razporeditev stroškovnih mest (analytic_distribution)se kopira na postavko
druga polja izvora na postavkosamodejno, če modul, ki jih zagotavlja, implementira dokumentirano kljuko Odoo
Izvor in referenca v glavi računazdružena, odstranjeni dvojniki, urejljiva v pomočniku
Prodajni nalog: invoice_ids, invoice_status, qty_invoicedse izračunajo iz postavk – naročilo novi račun prepozna samo
Beleženje časa (timesheet_invoice_id)se prenese na nov račun, preden viri postanejo neveljavni
Zahtevek helpdeskaje vezan na beleženje časa in tako samodejno kaže na nov račun
Chattersporočilo na novem računu z vsemi viri, sporočilo na vsakem viru s povezavo na novega
merged_into_move_id / merged_from_move_idsshranjena povezava v obe smeri

Davčne vrstice, vrstice plačilnih pogojev, vrstice zaokroževanja in vrstice popusta za takojšnje plačilo se ne kopirajo, temveč jih Odoo na novo izračuna iz vsebine. Združeni račun ima torej eno davčno vrstico na davčno stopnjo, ne eno na vir. Ustvari se neoštevilčen kot osnutek; samo združevanje ne porabi številke dokumenta.

Naslovi: stranka vidi, od kod izvira vsaka postavka

To je funkcija, ki v vsakdanu naredi razliko. Nad postavkami vsakega izvornega računa modul vstavi vrstico razdelka – isti element, ki ga Odoo sam uporablja za razdelek „Predplačila“:

BE10181                                     <- naslov
  22,500 x ura tehnika            3.577,50
                    Vmesna vsota  3.577,50
BE10182 · Zahtevek: HT00219                 <- naslov
   3,000 x ura tehnika              477,00
                    Vmesna vsota    477,00
                      NETO ZNESEK 4.054,50

Naslov se natisne na dokumentu, ki ga prejme stranka, standardno poročilo o računu Odoo pa samo doda vmesno vsoto za vsak razdelek. Stranka tako vidi tudi, koliko je stal vsak del.

Oznaka se sestavi iz tistega, kar vir dejansko ima:

  1. naročila, neposredno iz povezav postavk – imena so tako natančna; če povezav z naročili ni, se uporabi prosto besedilo iz invoice_origin
  2. zahtevki, ugotovljeni prek beleženja časa – izpisani kot Zahtevek: HT00219 ali Zahtevki: …, omejeno na pet plus „in še N drugih“
  3. če ni ničesar od tega: številka izvornega računa, nato njegova referenca
  4. če ni prav ničesar: nevtralno „Dodatne postavke“, da vrstice enega vira nikoli neopazno ne izginejo v bloku nad njimi

Kar manjka, se izpusti, ne izmisli. Zahtevek se natisne z lastno številko – številka helpdesk_mgmt, Enterprise ticket_ref ali prikazno ime modela – nikoli z ID-jem iz baze podatkov.

Naslovi se pišejo v jeziku stranke, tako kot to naredi Odoo pri ustvarjanju računov iz naročil. Chatter ostane v jeziku uporabnika.

Naslov ne nosi izdelka, količine, cene, davka, konta ali razporeditve stroškovnih mest. Ne more vplivati na vsote, davčno osnovo in stroškovno računovodstvo. Kdor naslove v pomočniku izklopi, dobi natanko račun, ki bi nastal brez te funkcije – test primerja oba rezultata do centa natančno.

Kaj se zgodi z izvornimi računi

Pomočnik ponuja tri možnosti:

  • Stornirati (privzeto, priporočeno). Storno osnutka je brez posledic: brez številke dokumenta, brez knjižbe. Računi ostanejo v zbirki podatkov kot stornirani dokumenti s chatterjem in povezavo na nov račun. qty_invoiced pri prodajnem nalogu preskoči stornirane dokumente, nič se ne šteje dvakrat. To je možnost, ki je razumljiva tudi čez šest mesecev.
  • Izbrisati. Nepreklicno. Chatter in povratna povezava izginejo skupaj z zapisom; ostane le sporočilo na novem računu. Smiselno za osnutke za enkratno rabo, ki jih nihče več ne želi videti.
  • Pustiti kot osnutek. Viri ostanejo odprti. Njihova prodajna naročila potem štejejo vir in nov račun kot fakturirana – delo je videti dvakrat obračunano, dokler nekdo osnutkov ne počisti ročno. Namenjeno redkemu primeru, ko želite pred odločitvijo primerjati. Test zajame prav to dvojno štetje, da ne preseneti nikogar.

Meje – česar modul namenoma ne počne

Prosimo, preberite pred produkcijsko uporabo:

  • Samo osnutki. Knjiženi računi se ne združujejo. To je naloga dobropisov, ne postopek kopiranja.
  • Enkrat knjiženi, vrnjeni osnutki se zavrnejo. Imajo številko in zgodovino knjiženja; skrivanje tega v drugem dokumentu ni nekaj, kar bi smela skupinska akcija opraviti tiho.
  • Brez združevanja prek več partnerjev, valut, podjetij ali dnevnikov. Za to ni smiselne enotne vrednosti, zato modul zavrne, namesto da bi ugibal.
  • payment_reference se ne prevzame. Strukturirane reference plačil identificirajo dokument; dve zlepljeni se ne ujemata z ničemer pri banki. Nov račun ob knjiženju dobi svojo.
  • Postavke predplačil se kopirajo kot vsaka druga postavka. Če viri vsebujejo vrstice predplačil iz modula sale, je treba rezultat pred knjiženjem preveriti.
  • Samo timesheet_invoice_id se samodejno prenese. Vsak drug model z Many2one na account.move (lastni obračunski zagoni, povezave naročnin, pošiljanje EDI) še naprej kaže na vir. Za to obstaja kljuka _merge_draft_relink.
  • Brez združevanja enakih postavk – namenoma. Kdor želi kratek, urejen račun, je tu na napačnem mestu in bi za to plačal s povezavami do izvora.
  • Naslovi nastanejo enkrat ob združevanju. Potem so običajne vrstice razdelkov: urejljive, izbrisljive in se ne posodabljajo, če se naročilo ali zahtevek pozneje preimenuje.
  • Naslov navaja zahtevke celotnega izvornega računa, ne posamezne postavke.

Namestitev v Odoo 19 Community

Namestitev poteka kot pri običajnem dodatnem modulu Odoo:

  1. Mapo modula kopirajte v nastavljeno pot dodatkov (addons).
  2. Posodobite seznam aplikacij v Odoo.
  3. Namestite modul account_invoice_merge_draft.

Modul je odvisen le od account. Vse, kar ve o sale, purchase, project, hr_timesheet, sale_timesheet ali modulu helpdeska, se ugotovi med izvajanjem – namesti se in deluje tudi na čisti obračunski zbirki podatkov.

Nato: Računovodstvo → Stranke → Računi (ali Dobavitelji → Računi), označite dva ali več osnutkov, meni z zobnikom → Združi osnutke računov. Dejanje je na voljo vsem uporabnikom s skupino Obračun / Fakturiranje.

Kdor posodablja s prejšnje različice modula, naj enkrat posodobi z -u account_invoice_merge_draft --i18n-overwrite, da spremenjene oznake polj pridejo v slovenski prevod. Nova namestitev tega ne zadeva.

Za razvijalce: API in kljuke

Združevanje je mogoče poklicati tudi iz lastne kode:

python

merged = draft_moves.merge_draft_moves(
    extra_vals={"invoice_date": date(2026, 3, 31), "ref": "Q1"},
    source_handling="cancel",   # "delete" ali "keep"
    add_source_sections=True,   # naslovi; privzeto True
)

merge_draft_moves sproži UserError z vsemi razlogi za zavrnitev, če izbora ni mogoče združiti. _merge_draft_get_errors() vrne iste razloge kot seznam brez izjeme.

Kdor ima lastne povratne reference na account.move, prepiše _merge_draft_relink:

python

class AccountMove(models.Model):
    _inherit = "account.move"

    def _merge_draft_relink(self, new_move):
        super()._merge_draft_relink(new_move)
        if "my_move_id" not in self.env["my.model"]._fields:
            return
        self.env["my.model"].search([("my_move_id", "in", self.ids)]).write(
            {"my_move_id": new_move.id}
        )

Kdor ima povratno referenco na postavki računa, tega modula sploh ne potrebuje: zadošča Odoojeva lastna kljuka account.move.line._copy_data_extend_business_fields in polje se prevzame enako pri združevanju, storniranju in podvajanju. Za druge module helpdeska ali drugačno formulacijo naslovov obstajata _merge_draft_get_source_tickets() in _merge_draft_section_name().

Preizkušeno, preden teče na produkcijskih podatkih

48 testov pokriva mešana stanja, različne partnerje, valute in vrste dokumentov, postavke z in brez prodajnega ali nabavnega naročila, davke, razdelke in opombe, razporeditve stroškovnih mest, prenos beleženja časa, vse tri možnosti za izvorne račune, pomočnika in naslove – da so privzeto vklopljeni, da njihov izklop do centa reproducira star rezultat, da naslov ne nosi zneskov ali kontov, da postavke enega vira ostanejo skupaj, da se zahtevek pojavi s svojo številko in nikoli z ID-jem iz baze podatkov.

Testni podatki temeljijo na obstoječem kontnem načrtu aktivnega podjetja namesto na AccountTestInvoicingCommon – ta pomočnik se na migriranih produkcijskih zbirkah podatkov pogosto ne zažene, modul pa mora teči prav na takih.

odoo-bin -d <db> -u account_invoice_merge_draft --test-enable \
         --test-tags /account_invoice_merge_draft --stop-after-init

Kot pri vsaki razširitvi na računovodskem področju priporočamo, da namestitev najprej preizkusite v testnem okolju s kopijo lastne zbirke podatkov.

Brezplačno in odprtokodno za skupnost Odoo Community

V družbi Biricon IT Services GmbH rešitve najprej razvijamo za konkretne zahteve iz lastnega poslovanja. Če iz tega nastane splošno uporaben modul, ga ne želimo skrivati v eni sami namestitvi.

Merge Draft Invoices je zato brezplačno na voljo kot odprtokodni projekt. Izvorno kodo je mogoče pregledati, preveriti, prilagoditi in v okviru licence AGPL-3 razvijati naprej.

Naš cilj ni bil najkrajši možni račun, temveč račun, pri katerem tudi čez leto dni vsaka postavka vodi nazaj do svojega naročila, svojega zahtevka in svoje knjižbe časa.

Projekt, izvorna koda in sodelovanje

Celotna izvorna koda, napotki za namestitev in tehnična dokumentacija so na voljo na GitHubu:

Odpri Odoo 19 Merge Draft Invoices na GitHubu

Veselimo se izkušenj iz prakse, poročil o napakah, predlogov za izboljšave in prispevkov skupnosti Odoo Community.

Brezplačno. Razumljivo. Odprta koda. Za Odoo 19 Community.