Novi Odoo modul: l10n_ba_edi_data — isključivanje fiskalizacije za interne prodajne žurnale (AVP, SL2, MANJ, OISP)
Problem
U bosanskoj lokalizaciji Odoo-a, žurnal “Primljeni avansi” (AVP) je tipa prodaja. To znači da modul l10n_ba_edi po defaultu uključi fiskalni EDI format ba_fiskalne_2_00 na njega — isto kao i na svaki drugi prodajni žurnal u BA kompanijama:
def _is_enabled_by_default_on_journal(self, journal):
self.ensure_one()
if self.code == "ba_fiskalne_2_00":
return journal.company_id.country_id.code == 'BA'
return super()._is_enabled_by_default_on_journal(journal)
Logika je razumna za stvarne prodajne žurnale (IF, IZA, …), ali za interne žurnale koji samo služe za PDV obračun to nije tačno. Primljeni avans, manjak ili interna potrošnja nikad ne idu na fiskalni uređaj — fiskalizacija tih iznosa je već urađena na izvornom dokumentu (faktura sa kojom je avans povezan, popis koji je generisao manjak, itd.).
Ako Fiskalizacija (BA) ostane uključena na ovim žurnalima, svaki put kad knjigovođa potvrdi takav nalog Odoo pokušava pozvati EDI server. To ili padne s greškom ili još gore — pošalje “fiskalni” račun koji nikad nije trebao da bude poslan.
Slika ispod prikazuje problem: žurnal “Primljeni avansi”, kartica “Napredna podešavanja”, sekcija Electronic Data Interchange — opcija Fiskalizacija (BA) je čekirana po defaultu (uokvireno crveno).

Šta radi l10n_ba_edi_data
Novi mali “glue” modul koji premošćuje dva nezavisna modula:
l10n_ba_data— definira žurnale (AVP, SL2, MANJ, OISP, …)l10n_ba_edi— definira fiskalni EDI format
Nijedan od njih ne vidi onaj drugi (i ne treba — kontni plan nema potrebe znati za fiskalizaciju, a fiskalizacija nema potrebe znati za konkretan kontni plan). Glue modul zavisi od oba i postavlja konfiguraciju koja je tačka spoja:
NO_FISCAL_JOURNAL_CODES = ("AVP", "SL2", "MANJ", "OISP")
BA_FISCAL_EDI_XMLID = "l10n_ba_edi.edi_in_einvoice_json_1_03"
def disable_fiscal_on_no_fiscal_journals(env):
fiscal_format = env.ref(BA_FISCAL_EDI_XMLID, raise_if_not_found=False)
if not fiscal_format:
return
journals = env["account.journal"].search([
("code", "in", list(NO_FISCAL_JOURNAL_CODES)),
("edi_format_ids", "in", fiscal_format.id),
])
for journal in journals:
journal.edi_format_ids = [(3, fiscal_format.id)]
Žurnali koji se čiste:
| Kod | Naziv | Tip | Razlog |
|---|---|---|---|
AVP | Primljeni avansi | sale | PDV obračun avansa, ne odlazi na fiskalni uređaj |
SL2 | Umanjenje PDV po PDV-SL-2 | sale | Knjiženje umanjenja po PDV-SL-2, interno |
MANJ | Manjak 07 | sale | Knjiženje manjka iz popisa, interno |
OISP | Ostale isporuke | sale | Ostale isporuke za PDV obračun, već fiskalizirane na izvoru |
Tehničke odluke
auto_install=True
Modul se aktivira čim su l10n_ba_data i l10n_ba_edi instalirani — bez potrebe da neko “ne zaboravi” da ga instalira. To je tipičan Odoo pattern za “X_Y” module koji premošćuju dvije nezavisne tačke.
post_init_hook + post-migrate.py
post_init_hookpokriva svježu instalacijupost-migrate.pypokriva nadogradnje i dodavanje novih kodova uNO_FISCAL_JOURNAL_CODES
Svaki put kad povećamo verziju modula (npr. dodamo novi kod kao “DON” za izvršene donacije), post-migrate ponovo pokrene cleanup i počisti i nove pogođene žurnale.
Pretraga po kodu, ne po xml_id
account.journal zapisi se generišu po kompaniji. Da smo pisali env.ref('l10n_ba_data.journal_avp') uhvatili bismo samo jednu kompaniju. Pretraga [("code", "in", ...)] radi kroz sve kompanije u jednoj operaciji — multi-company safe iz prve.
Idempotentno čišćenje (3, id)
Komanda (3, fiscal_format.id) u Odoo M2M sintaksi znači “ukloni vezu na ovaj jedan format, ostavi sve druge na miru”. Ako neko ima i Peppol BIS Billing 3.0 uključen na istom žurnalu, on ostaje — uklanja se samo ba_fiskalne_2_00. Sigurno za ponavljanje na svakom upgrade-u.
Filter ("edi_format_ids", "in", fiscal_format.id) u pretrazi
Pretraga gleda samo one žurnale koji trenutno imaju format uključen. Bez tog filtra, modul bi pisao na svaki AVP/SL2/MANJ/OISP žurnal i nepotrebno trigerirao write() događaje (cache invalidacija, modified tracking, …). Sa filterom, prazan run je stvarno prazan run.
Rezultat na produkciji
Prije instalacije (provjera u produkciji odoo-bringout-1):
company | code | journal_name | edi_formats
------------------------+------+---------------------------+------------------
bring.out doo Sarajevo | AVP | Primljeni avansi | ba_fiskalne_2_00
bring.out doo Sarajevo | MANJ | Manjak 07 | ba_fiskalne_2_00
bring.out doo Sarajevo | OISP | Ostale isporuke | ba_fiskalne_2_00
bring.out doo Sarajevo | SL2 | Umanjenje PDV po PDV-SL-2 | ba_fiskalne_2_00
Nakon instalacije:
company | code | journal_name | edi_formats
------------------------+------+---------------------------+-------------
bring.out doo Sarajevo | AVP | Primljeni avansi | (none)
bring.out doo Sarajevo | MANJ | Manjak 07 | (none)
bring.out doo Sarajevo | OISP | Ostale isporuke | (none)
bring.out doo Sarajevo | SL2 | Umanjenje PDV po PDV-SL-2 | (none)
Zašto ovo nije bilo u l10n_ba_data ili l10n_ba_edi
Razmatrali smo tri opcije prije nego što smo se odlučili za zaseban modul:
Opcija 1 — staviti u l10n_ba_data/account_journals.xml: zahtijevalo bi da kontni plan zavisi od fiskalizacije. Pogrešan smjer zavisnosti — kontni plan je fundamentalniji od EDI integracije.
Opcija 2 — override _is_enabled_by_default_on_journal u l10n_ba_edi: fiskalizacijski modul bi morao znati za konkretne kodove žurnala koje definira drugi modul. Curi se konkretna shema kontnog plana u generički EDI sloj.
Opcija 3 — novi l10n_ba_edi_data (izabrano): konfiguracija živi tačno na presjeku ta dva svijeta. Lako se proširi (dodaš kod u tuple), lako se preskoči (ne instaliraš modul ako fiskalizaciju ne koristiš), i ne pogađa nijedan od dva osnovna modula.
Šta dalje
Ovaj modul je trenutno pokriva četiri najevidentnija slučaja. Postoje i drugi prodajni žurnali koji su u sličnoj situaciji (npr. INTP — Interna potrošnja, DON — Izvršene donacije, REIF — Re-export prodaja), ali za neke od njih treba računovodstvena diskusija prije nego što ih dodamo. Kad to razjasnimo, bukvalno je dopisivanje jednog stringa u tuple NO_FISCAL_JOURNAL_CODES i bump verzije.
Napomena
Generisano od strane Claude 🤖
Ernad Husremović, hernad@bring.out.ba