Preskoči na sadržaj

Odoo 19 Community: spajanje nacrta računa – naš open source modul Merge Draft Invoices

18. rujna 2026. od
Odoo 19 Community: spajanje nacrta računa – naš open source modul Merge Draft Invoices
Alexander Biringer | Biricon IT Services GmbH

Tko u Odoo-u obračunava iz narudžbi, helpdesk tiketa i evidencije vremena, poznaje rezultat: kupac na kraju mjeseca ne dobiva jedan račun, nego pet. Svaki potječe iz druge narudžbe ili tiketa, svaki je stručno ispravan – a ipak kupac pita ne bi li sve stalo u jedan dokument.

Upravo za to u Odoo 19 Community ne postoji čisto rješenje. Poznati OCA modul account_invoice_merge još nije objavljen za Odoo 19, a problem rješava na način koji za pružatelje usluga predstavlja ozbiljan rizik: iste se stavke objedinjuju, čime se gubi veza natrag s narudžbom, zadatkom i evidentiranim vremenom.

Za vlastitu instalaciju Odoo 19 Community stoga smo razvili modul Merge Draft Invoices. Budući da taj problem ne pogađa samo nas, besplatno ga stavljamo na raspolaganje na GitHubu pod slobodnom licencom AGPL-3.

Na projekt na GitHubu:

Odoo 19 Merge Draft Invoices

Zašto vlastiti modul umjesto OCA rješenja?

OCA već dugi niz godina održava account_invoice_merge u repozitoriju OCA/account-invoicing. U trenutku objave ovog članka migracija na 19.0 tamo je još otvoren pull request, a ne objavljen modul.

Važnije je, međutim, to što ta dva modula rade različite stvari:


OCA account_invoice_mergeMerge Draft Invoices
Rezultatspaja se u jedan od odabranih računauvijek novi račun
Stavkeiste se stavke objedinjuju, količine se zbrajajusvaka se stavka preuzima jedan prema jedan
Podrijetloinvoice_origin / payment_reference u zaglavlju spojeni jedno za drugimpolja podrijetla po stavci kopiraju se putem Odoo-ova vlastitog _copy_data_extend_business_fields hooka
Evidencija vremenanije uzeta u obzirtimesheet_invoice_id prebacuje se na novi račun
Čitljivo za kupcaništanaslov iznad stavki svakog izvora s narudžbom i tiketom
Izvorni računise stornirajustornirati (standardno), izbrisati ili zadržati – s jasno imenovanim posljedicama

Objedinjavanje istih stavki ključna je razlika. Objedinjena stavka može imati samo jedno podrijetlo. Čim dva retka iz dviju različitih narudžbi izgledaju jednako, veza od računa natrag do narudžbe, zadatka i knjiženja vremena nestaje. U uslužnoj tvrtki u kojoj se obračun odvija putem tiket → evidencija vremena → račun to znači: isti se sati naplaćuju dvaput ili nestaju.

Naš modul stoga načelno ništa ne objedinjuje. Spojeni račun ima točno onoliko stavki koliko ih imaju njegovi izvori zajedno.

Nikakav kôd nije preuzet iz OCA modula. Oba modula su pod licencom AGPL-3.

Što modul radi

U prikazu popisa računa označiti dva ili više nacrta, u izborniku radnji odabrati Spoji nacrte računa – čarobnjak prikazuje odabir, provjere i opcije za izvorne račune. Nakon potvrde odmah se dolazi na novi račun.

Prije spajanja modul provjerava, i to sve točke odjednom, kako se ne bi nakon svakog ispravka vidjela sljedeća pogreška:

  • odabrana najmanje dva računa
  • svi u statusu Nacrt
  • nijedan nikada nije bio knjižen (dokument vraćen u nacrt zadržava povijest knjiženja i odbija se)
  • nijedan nije već bio spojen
  • svi su račun, ulazni račun, odobrenje ili dokument – ne čisti nalozi za knjiženje
  • isti partner, vrsta dokumenta, tvrtka, valuta i dnevnik

Razlike u uvjetima plaćanja, dodjeli poreza, adresi isporuke, bankovnom računu ili prodavaču ne blokiraju. Čarobnjak upozorava i preuzima vrijednost najstarijeg odabranog računa.

Što se zadržava

Srž modula je u tome da svaka veza koju stavka računa ima prema svom podrijetlu postoji i na novom računu:

PovezivanjePostupanje
Stavke prodajne narudžbe (sale_line_ids)po stavci se kopira putem Odoo-ova _copy_data_extend_business_fields
Stavke narudžbenice (purchase_line_id)jednako
Raspodjela po mjestima troška (analytic_distribution)kopira se po stavci
ostala polja podrijetla po stavciautomatski, ako modul koji ih isporučuje implementira dokumentirani Odoo hook
Podrijetlo i referenca u zaglavlju računaspojeno, deduplicirano, može se uređivati u čarobnjaku
Prodajna narudžba: invoice_ids, invoice_status, qty_invoicedizračunavaju se iz stavki – narudžba sama prepoznaje novi račun
Evidencija vremena (timesheet_invoice_id)prebacuje se na novi račun prije nego što izvori postanu nevažeći
Helpdesk tiketvezan je uz evidenciju vremena i tako automatski upućuje na novi račun
Chatterporuka na novom računu sa svim izvorima, poruka na svakom izvoru s poveznicom na novi
merged_into_move_id / merged_from_move_idspohranjena veza u oba smjera

Porezni retci, retci uvjeta plaćanja, retci zaokruživanja i skonta ne kopiraju se, nego ih Odoo iznova izračunava iz sadržaja. Spojeni račun stoga ima jedan porezni redak po poreznoj stopi, a ne jedan po izvoru. Izrađuje se kao nacrt bez broja; samo spajanje ne troši broj dokumenta.

Naslovi: kupac vidi odakle potječe svaka stavka

To je funkcija koja u svakodnevici čini razliku. Iznad stavki svakog izvornog računa modul postavlja redak odjeljka – isti element koji Odoo sam koristi za odjeljak „Predujmovi“:

BE10181                                     <- naslov
  22,500 x Sat tehničara          3.577,50
                    Međuzbroj     3.577,50
BE10182 · Tiket: HT00219                    <- naslov
   3,000 x Sat tehničara            477,00
                    Međuzbroj       477,00
                      NETO IZNOS  4.054,50

Naslov se ispisuje na dokumentu koji kupac dobiva, a Odoo-ovo standardno izvješće o računu samo od sebe dodaje međuzbroj po odjeljku. Kupac tako vidi i koliko je koštao svaki dio.

Oznaka se sastavlja iz onoga što izvor stvarno ima:

  1. narudžbe, izravno iz poveznica stavki – nazivi su stoga točni; ako nedostaju poveznice s narudžbama, koristi se slobodan tekst iz invoice_origin
  2. tiketi, utvrđeni putem evidencije vremena – ispisani kao Tiket: HT00219 ili Tiketi: …, ograničeno na pet plus „i još N“
  3. ako nema ni jednog ni drugog: broj izvornog računa, zatim njegova referenca
  4. ako nema ničega: neutralno „Ostale stavke“, kako reci jednog izvora nikada ne bi neprimjetno nestali u bloku iznad

Što nedostaje, izostavlja se, ne izmišlja se. Tiket se ispisuje s vlastitim brojem – helpdesk_mgmt broj, Enterprise ticket_ref ili prikazni naziv modela – nikada s ID-jem baze podataka.

Naslovi se pišu na jeziku kupca, kao što to Odoo čini pri izradi računa iz narudžbi. Chatter ostaje na jeziku korisnika.

Naslov ne nosi proizvod, količinu, cijenu, porez, konto ni raspodjelu po mjestima troška. Ne može utjecati na zbrojeve, poreznu osnovicu i obračun troškova. Tko u čarobnjaku isključi naslove, dobiva točno onaj račun koji bi nastao bez ove funkcije – test uspoređuje oba rezultata do centa.

Što se događa s izvornim računima

Čarobnjak nudi tri opcije:

  • Stornirati (standardno, preporučeno). Storniranje nacrta nema posljedica: nema broja dokumenta, nema knjiženja. Računi ostaju u bazi podataka kao stornirani dokumenti s chatterom i poveznicom na novi račun. qty_invoiced na prodajnoj narudžbi preskače stornirane dokumente, ništa se ne broji dvaput. To je opcija koja je razumljiva i za šest mjeseci.
  • Izbrisati. Neopozivo. Chatter i povratna referenca nestaju sa zapisom; ostaje samo poruka na novom računu. Smisleno za privremene nacrte koje nitko više ne treba vidjeti.
  • Ostaviti kao nacrt. Izvori ostaju otvoreni. Njihove prodajne narudžbe tada računaju izvor i novi račun kao fakturirane – rad izgleda dvaput obračunat dok netko ručno ne počisti nacrte. Namijenjeno rijetkom slučaju kada se prije odluke želi usporediti. Test bilježi upravo to dvostruko brojanje kako nikoga ne bi iznenadilo.

Granice – što modul namjerno ne radi

Molimo pročitajte prije produktivne uporabe:

  • Samo nacrti. Knjiženi računi ne spajaju se. To je zadatak za odobrenja, a ne postupak kopiranja.
  • Jednom knjiženi, vraćeni nacrti se odbijaju. Oni imaju broj i povijest knjiženja; skrivati to u drugom dokumentu nije nešto što bi skupna radnja trebala tiho obaviti.
  • Nema spajanja preko različitih partnera, valuta, tvrtki ili dnevnika. Za to ne postoji smislena pojedinačna vrijednost, pa modul odbija umjesto da pogađa.
  • payment_reference se ne preuzima. Strukturirane reference plaćanja identificiraju dokument; dvije spojene jedna za drugom ne odgovaraju ničemu u banci. Novi račun pri knjiženju dobiva vlastitu.
  • Stavke predujma kopiraju se kao i svaka druga stavka. Ako izvori sadrže retke predujma iz modula sale, rezultat treba provjeriti prije knjiženja.
  • Automatski se prebacuje samo timesheet_invoice_id. Svaki drugi model s poljem Many2one na account.move (vlastiti obračunski ciklusi, poveznice s pretplatama, EDI dostave) i dalje upućuje na izvor. Za to postoji hook _merge_draft_relink.
  • Nema objedinjavanja istih stavki – namjerno. Tko želi kratak, uredan račun, ovdje je na pogrešnom mjestu i za to bi platio vezama podrijetla.
  • Naslovi nastaju jednokratno pri spajanju. Nakon toga su to normalni retci odjeljaka: mogu se uređivati, brisati i ne ažuriraju se ako se narudžba ili tiket kasnije preimenuju.
  • Naslov navodi tikete cijelog izvornog računa, a ne pojedine stavke.

Instalacija u Odoo 19 Community

Instalacija se provodi kao kod uobičajenog dodatnog modula za Odoo:

  1. Kopirati direktorij modula u konfiguriranu putanju addonsa.
  2. Ažurirati popis aplikacija u Odoo-u.
  3. Instalirati modul account_invoice_merge_draft.

Modul ovisi samo o modulu account. Sve što zna o modulima sale, purchase, project, hr_timesheet, sale_timesheet ili helpdesk modulu utvrđuje se za vrijeme izvođenja – instalira se i radi i na čistoj obračunskoj bazi podataka.

Zatim: Računovodstvo → Kupci → Računi (ili Dobavljači → Računi), označiti dva ili više nacrta, izbornik zupčanika → Spoji nacrte računa. Radnja je dostupna svim korisnicima s grupom Obračun / Fakturiranje.

Tko ažurira s ranije verzije modula, trebao bi jednokratno ažurirati s -u account_invoice_merge_draft --i18n-overwrite kako bi izmijenjeni nazivi polja stigli u njemački prijevod. Nova instalacija time nije pogođena.

Za programere: API i hookovi

Spajanje se može pozvati i iz vlastitog kôda:

python

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

merge_draft_moves baca UserError sa svim razlozima odbijanja ako se odabir ne može spojiti. _merge_draft_get_errors() vraća iste razloge kao popis bez iznimke.

Tko ima vlastite povratne reference na account.move, nadjačava _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}
        )

Tko ima povratnu referencu na stavci računa, ovaj modul uopće ne treba: Odoo-ov vlastiti hook account.move.line._copy_data_extend_business_fields je dovoljan, a polje se jednako preuzima pri spajanju, storniranju i dupliciranju. Za druge helpdesk module ili drukčiju formulaciju naslova postoje _merge_draft_get_source_tickets() i _merge_draft_section_name().

Testirano prije nego što se pokrene na produktivnim podacima

48 testova pokriva miješane statuse, različite partnere, valute i vrste dokumenata, stavke s prodajnom ili nabavnom narudžbom i bez nje, poreze, odjeljke i bilješke, raspodjele po mjestima troška, prebacivanje evidencije vremena, sve tri opcije za izvorne račune, čarobnjak i naslove – da su standardno aktivni, da njihovo isključivanje reproducira stari rezultat do centa, da naslov ne nosi iznose ni konta, da stavke jednog izvora ostaju zajedno, da se tiket pojavljuje sa svojim brojem, a nikada s ID-jem baze podataka.

Testni podaci temelje se na postojećem kontnom planu aktivne tvrtke, a ne na AccountTestInvoicingCommon – taj helper na migriranim produktivnim bazama podataka često ne pokreće, a modul treba raditi upravo na takvima.

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

Kao i kod svakog proširenja u računovodstvenom području, preporučujemo da se instalacija najprije provjeri u testnom okruženju s kopijom vlastite baze podataka.

Besplatno i open source za Odoo Community

Mi u Biricon IT Services GmbH rješenja najprije razvijamo za konkretne zahtjeve iz vlastitog poslovanja. Ako iz toga nastane općenito upotrebljiv modul, ne želimo ga skrivati u jednoj instalaciji.

Merge Draft Invoices stoga je besplatno dostupan kao open source projekt. Izvorni se kôd može pregledati, provjeriti, prilagoditi i dalje razvijati u okviru licence AGPL-3.

Naš cilj nije bio što kraći račun, nego račun kod kojeg se i za godinu dana svaka stavka može pratiti natrag do svoje narudžbe, svog tiketa i svog knjiženja vremena.

Projekt, izvorni kôd i suradnja

Cjelokupni izvorni kôd, upute za instalaciju i tehnička dokumentacija dostupni su na GitHubu:

Otvoriti Odoo 19 Merge Draft Invoices na GitHubu

Veseli nas iskustva iz prakse, izvješća o pogreškama, prijedlozi poboljšanja i doprinosi iz Odoo zajednice.

Besplatno. Razumljivo. Open source. Za Odoo 19 Community.