Aki az Odoo-ban megrendelésekből, helpdesk-jegyekből és időnyilvántartásból számláz, ismeri az eredményt: egy ügyfél a hónap végén nem egy, hanem öt számlát kap. Mindegyik másik megrendelésből vagy jegyből származik, mindegyik szakmailag helyes – és az ügyfél mégis megkérdezi, hogy nem férne-e el mindez egyetlen dokumentumon.
Pontosan erre nincs tiszta megoldás az Odoo 19 Community-ben. Az ismert OCA-modul, az account_invoice_merge az Odoo 19-hez még nincs kiadva, és olyan módon oldja meg a problémát, amely a szolgáltatók számára komoly kockázatot jelent: az azonos tételeket összevonja, és ezzel elvész a kapcsolat a megrendeléshez, a feladathoz és a rögzített időhöz.
Saját Odoo 19 Community-telepítésünkhöz ezért kifejlesztettük a Merge Draft Invoices modult. Mivel ez a probléma nem csak minket érint, a szabad AGPL-3-licenc alatt ingyenesen elérhetővé tesszük a GitHubon.
A projekthez a GitHubon:
Miért saját modul az OCA-megoldás helyett?
Az OCA évek óta gondozza az account_invoice_merge modult az OCA/account-invoicing tárolóban. E bejegyzés időpontjában a 19.0-ra történő migráció ott még nyitott pull request, nem pedig közzétett modul.
Fontosabb azonban, hogy a két modul különböző dolgokat csinál:
| OCA account_invoice_merge | Merge Draft Invoices | |
|---|---|---|
| Eredmény | a kiválasztott számlák egyikébe vonja össze | mindig egy új számla |
| Tételek | az azonos tételeket összevonja, a mennyiségeket összeadja | minden tételt egy az egyben átvesz |
| Eredet | invoice_origin / payment_reference a fejlécben összefűzve | az eredetmezők tételenként az Odoo saját _copy_data_extend_business_fields hookján keresztül másolva |
| Időnyilvántartás | nem veszi figyelembe | a timesheet_invoice_id átkerül az új számlára |
| Olvasható az ügyfél számára | semmi | fejléc a tételek felett minden forrásnál, a megrendeléssel és a jeggyel |
| Forrásszámlák | sztornózásra kerülnek | sztornózás (alapértelmezett), törlés vagy megtartás – egyértelműen megnevezett következményekkel |
Az azonos tételek összevonása a döntő különbség. Egy összevont tétel csak egy eredetet hordozhat. Amint két különböző megrendelésből származó két sor egyformának látszik, megszakad a kapcsolat a számlától vissza a megrendelésig, a feladatig és az időkönyveléshez. Egy olyan szolgáltató vállalkozásnál, ahol az elszámolás a Ticket → Időnyilvántartás → Számla útvonalon fut, ez azt jelenti: ugyanazokat az órákat kétszer számlázzák ki, vagy eltűnnek.
Modulunk ezért alapvetően semmit nem von össze. Az összevont számlának pontosan annyi tétele van, mint a forrásainak együttvéve.
Az OCA-modulból nem vettünk át kódot. Mindkét modul AGPL-3 alatt áll.
Mit csinál a modul
A számlalista nézetben jelöljön ki két vagy több piszkozatot, a műveleti menüben válassza a Piszkozatszámlák összevonása lehetőséget – egy varázsló megmutatja a kijelölést, az ellenőrzéseket és a forrásszámlákra vonatkozó beállításokat. A megerősítés után közvetlenül az új számlán köt ki.
Az összevonás előtt a modul ellenőriz, mégpedig az összes pontot egyszerre, hogy ne minden javítás után a következő hibát lássuk:
- legalább két számla kijelölve
- mind Piszkozat állapotban
- egyiket sem könyvelték soha (a piszkozatra visszaállított bizonylat megtartja a könyvelési előzményeit, és elutasításra kerül)
- egyiket sem vonták még össze
- mind számla, szállítói számla, jóváíró számla vagy bizonylat – nem tisztán könyvelési tételek
- azonos partner, bizonylattípus, vállalkozás, pénznem és napló
A fizetési feltételben, adó-hozzárendelésben, szállítási címben, bankszámlában vagy értékesítőben mutatkozó eltérések nem blokkolnak. A varázsló figyelmeztet, és a legrégebbi kijelölt számla értékét veszi át.
Ami megmarad
A modul lényege, hogy minden kapcsolat, amelyet egy számlatétel az eredetéhez fűz, az új számlán is létezik:
| Kapcsolat | Kezelés |
|---|---|
| Értékesítési megrendelés tételei (sale_line_ids) | tételenként az Odoo _copy_data_extend_business_fields hookján keresztül másolva |
| Beszerzési megrendelés tételei (purchase_line_id) | ugyanígy |
| Költséghelyi megosztás (analytic_distribution) | tételenként másolva |
| egyéb eredetmezők tételenként | automatikusan, ha a szállító modul implementálja a dokumentált Odoo-hookot |
| Eredet és hivatkozás a számla fejlécében | összevonva, duplikátumoktól megtisztítva, a varázslóban szerkeszthető |
| Értékesítési megrendelés: invoice_ids, invoice_status, qty_invoiced | a tételekből kerülnek kiszámításra – a megrendelés magától felismeri az új számlát |
| Időnyilvántartás (timesheet_invoice_id) | az új számlára kerül át, mielőtt a források érvénytelenné válnak |
| Helpdesk-jegy | az időnyilvántartáshoz kapcsolódik, és ezáltal automatikusan az új számlára mutat |
| Chatter | üzenet az új számlán az összes forrással, üzenet minden forráson az újra mutató hivatkozással |
| merged_into_move_id / merged_from_move_ids | mentett kapcsolat mindkét irányban |
Az adósorokat, fizetési feltételi sorokat, kerekítési és skontósorokat nem másolja, hanem az Odoo számolja újra a tartalomból. Az összevont számlának tehát adókulcsonként egy adósora van, nem forrásonként egy. Számozatlan piszkozatként jön létre; maga az összevonás nem használ fel bizonylatszámot.
Fejlécek: az ügyfél látja, honnan származik az egyes tételek
Ez az a funkció, amely a mindennapokban különbséget jelent. Az egyes forrásszámlák tételei fölé a modul egy szakaszsort helyez – ugyanazt az elemet, amelyet az Odoo maga is használ az „Előlegek” szakaszhoz:
BE10181 <- Fejléc
22,500 x Technikusi óra 3.577,50
Részösszeg 3.577,50
BE10182 · Ticket: HT00219 <- Fejléc
3,000 x Technikusi óra 477,00
Részösszeg 477,00
NETTÓ ÖSSZEG 4.054,50
A fejléc rákerül az ügyfélnek átadott dokumentumra, és az Odoo szabványos számlajelentése magától kiegészít egy részösszeget szakaszonként. Így az ügyfél azt is látja, mennyibe került az egyes rész.
A felirat abból áll össze, amivel a forrás ténylegesen rendelkezik:
- a megrendelések, közvetlenül a tételkapcsolatokból – a nevek így pontosak; ha hiányoznak a megrendelés-kapcsolatok, az invoice_origin szabad szövege kerül felhasználásra
- a ticketek, az időnyilvántartáson keresztül meghatározva – megjelenítve Ticket: HT00219 vagy Tickets: … formában, legfeljebb ötre korlátozva, plusz „és N további”
- ha egyik sincs: a forrásszámla száma, majd a hivatkozása
- ha semmi sincs: egy semleges „További tételek”, hogy egy forrás sorai soha ne tűnjenek el csendben a fölötte lévő blokkban
Ami hiányzik, azt elhagyja, nem találja ki. A ticket a saját számával kerül kinyomtatásra – helpdesk_mgmt-szám, Enterprise-ticket_ref vagy a modell megjelenített neve – soha nem az adatbázis-azonosítóval.
A fejlécek az ügyfél nyelvén íródnak, ahogyan az Odoo is teszi, amikor megrendelésekből számlákat hoz létre. A Chatter a felhasználó nyelvén marad.
Egy fejléc nem hordoz terméket, mennyiséget, árat, adót, számlát és költséghelyi megosztást. Nem befolyásolhatja az összegeket, az adóalapot és a költségszámítást. Aki a varázslóban kikapcsolja a fejléceket, pontosan azt a számlát kapja, amely ezen funkció nélkül keletkezne – egy teszt fillérre pontosan összeveti a két eredményt.
Mi történik a forrásszámlákkal
A varázsló három lehetőséget kínál:
- Sztornózás (alapértelmezett, ajánlott). Egy piszkozat sztornózása következmények nélküli: nincs bizonylatszám, nincs könyvelés. A számlák sztornózott bizonylatként, Chatterrel és az új számlára mutató hivatkozással maradnak az adatbázisban. Az értékesítési megrendelés qty_invoiced mezője átugorja a sztornózott bizonylatokat, semmi nem számít kétszer. Ez az a lehetőség, amely hat hónap múlva is követhető.
- Törlés. Visszavonhatatlan. A Chatter és a visszahivatkozás az adatrekorddal együtt megszűnik; csak az új számlán lévő üzenet marad. Értelmes az eldobható piszkozatoknál, amelyeket senkinek sem kell többé látnia.
- Piszkozatként hagyás. A források nyitva maradnak. Értékesítési megrendeléseik ekkor a forrást és az új számlát is kiszámlázottként számolják – a munka kétszer elszámoltnak látszik, amíg valaki kézzel ki nem takarítja a piszkozatokat. Arra a ritka esetre szánt, amikor a döntés előtt össze szeretnénk hasonlítani. Egy teszt éppen ezt a kettős számolást rögzíti, hogy ne lepjen meg senkit.
Korlátok – amit a modul szándékosan nem tesz
Kérjük, éles használat előtt olvassa el:
- Csak piszkozatok. A könyvelt számlákat nem vonja össze. Ez jóváíró számlás feladat, nem másolási folyamat.
- Az egyszer könyvelt, visszaállított piszkozatokat elutasítja. Van számuk és könyvelési előzményük; ennek egy másik dokumentumban való elrejtése nem olyasmi, amit egy tömeges művelet csendben elintézhet.
- Nincs összevonás partnerek, pénznemek, vállalkozások vagy naplók között. Ehhez nincs értelmes egyetlen érték, ezért a modul elutasítja, ahelyett hogy találgatna.
- A payment_reference nem kerül átvételre. A strukturált fizetési hivatkozások egy bizonylatot azonosítanak; kettő összefűzve semmire sem illik a banknál. Az új számla könyveléskor megkapja a sajátját.
- Az előlegtételeket úgy másolja, mint bármely más tételt. Ha a források a sale modulból származó előlegsorokat tartalmaznak, az eredményt könyvelés előtt ellenőrizni kell.
- Csak a timesheet_invoice_id kerül automatikusan átkötésre. Minden más, account.move-ra mutató Many2one-nal rendelkező modell (saját elszámolási futások, előfizetési kapcsolatok, EDI-átviteli folyamatok) továbbra is a forrásra mutat. Erre való a _merge_draft_relink hook.
- Nincs azonos tételek összevonása – szándékosan. Aki rövid, rendezett számlát szeretne, rossz helyen jár, és az eredetkapcsolatokkal fizetne érte.
- A fejlécek egyszer, az összevonáskor keletkeznek. Ezután normál szakaszsorok: szerkeszthetők, törölhetők, és nem frissülnek, ha a megrendelést vagy a ticketet később átnevezik.
- Egy fejléc a teljes forrásszámla ticketjeit nevezi meg, nem az egyes tételekét.
Telepítés az Odoo 19 Community-ben
A telepítés ugyanúgy történik, mint egy szokásos Odoo-kiegészítő modulnál:
- A modulkönyvtárat másolja be egy beállított addons-útvonalra.
- Frissítse az alkalmazáslistát az Odoo-ban.
- Telepítse az account_invoice_merge_draft modult.
A modul csak az account-tól függ. Mindazt, amit a sale, purchase, project, hr_timesheet, sale_timesheet vagy egy helpdesk-modul felől tud, futásidőben határozza meg – egy tisztán számlázási adatbázison is települ és működik.
Ezután: Könyvelés → Vevők → Számlák (vagy Szállítók → Számlák), jelöljön ki két vagy több piszkozatot, fogaskerék-menü → Piszkozatszámlák összevonása. A művelet minden olyan felhasználó számára elérhető, aki az Elszámolás / Számlázás csoport tagja.
Aki a modul egy korábbi verziójáról frissít, egyszer futtassa a -u account_invoice_merge_draft --i18n-overwrite frissítést, hogy a módosított mezőmegnevezések megjelenjenek a német fordításban. Az új telepítést ez nem érinti.
Fejlesztőknek: API és hookok
Az összevonás saját kódból is meghívható:
python
merged = draft_moves.merge_draft_moves(
extra_vals={"invoice_date": date(2026, 3, 31), "ref": "Q1"},
source_handling="cancel", # "delete" vagy "keep"
add_source_sections=True, # fejlécek; alapértelmezett True
)
A merge_draft_moves UserError-t dob az összes elutasítási okkal, ha a kijelölés nem vonható össze. A _merge_draft_get_errors() ugyanezeket az okokat listaként adja vissza, kivétel nélkül.
Aki saját visszahivatkozásokkal rendelkezik az account.move-ra, felülírja a _merge_draft_relink-et:
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}
)
Aki a számlatételen rendelkezik visszahivatkozással, azoknak erre a modulra egyáltalán nincs szükségük: az Odoo saját account.move.line._copy_data_extend_business_fields hookja elegendő, és a mező összevonáskor, sztornózáskor és duplikáláskor egyaránt átvételre kerül. Más helpdesk-modulokhoz vagy a fejlécek másféle megfogalmazásához ott van a _merge_draft_get_source_tickets() és a _merge_draft_section_name().
Tesztelve, mielőtt éles adatokon futna
48 teszt fedi le a vegyes állapotokat, a különböző partnereket, pénznemeket és bizonylattípusokat, az értékesítési vagy beszerzési megrendeléssel rendelkező és azok nélküli tételeket, az adókat, szakaszokat és jegyzeteket, a költséghelyi megosztásokat, az időnyilvántartás átkötését, a forrásszámlákra vonatkozó mindhárom lehetőséget, a varázslót és a fejléceket – hogy alapértelmezetten aktívak, hogy kikapcsolásuk fillérre pontosan reprodukálja a régi eredményt, hogy egy fejléc nem hordoz összegeket vagy számlákat, hogy egy forrás tételei együtt maradnak, hogy a ticket a saját számával jelenik meg, és soha nem az adatbázis-azonosítóval.
A tesztadatok az aktív vállalkozás meglévő számlatükrére épülnek, nem az AccountTestInvoicingCommonra – ez a helper a migrált éles adatbázisokon gyakran nem indul el, és a modulnak éppen ilyeneken kell futnia.
odoo-bin -d <db> -u account_invoice_merge_draft --test-enable \
--test-tags /account_invoice_merge_draft --stop-after-init
Mint minden könyvelési területen alkalmazott bővítménynél, javasoljuk, hogy a telepítést először tesztkörnyezetben, a saját adatbázis másolatával ellenőrizze.
Ingyenes és nyílt forráskódú az Odoo Community számára
Mi, a Biricon IT Services GmbH-nál, a megoldásokat először a saját üzemünk konkrét igényeire fejlesztjük. Ha ebből általánosan használható modul születik, nem szeretnénk egyetlen telepítésben elrejteni.
A Merge Draft Invoices ezért ingyenesen, nyílt forráskódú projektként elérhető. A forráskód megtekinthető, ellenőrizhető, módosítható és az AGPL-3-licenc keretein belül továbbfejleszthető.
Célunk nem a lehető legrövidebb számla volt, hanem egy olyan számla, amelyen egy év múlva is minden tétel visszavezet a megrendeléséhez, a ticketjéhez és az időkönyveléséhez.
Projekt, forráskód és közreműködés
A teljes forráskód, a telepítési útmutató és a műszaki dokumentáció elérhető a GitHubon:
Az Odoo 19 Merge Draft Invoices megnyitása a GitHubon
Örülünk a gyakorlati tapasztalatoknak, hibajelentéseknek, fejlesztési javaslatoknak és az Odoo Community hozzájárulásainak.
Ingyenes. Követhető. Nyílt forráskódú. Az Odoo 19 Community számára.