Bosanska lokalizacija "Odoo" open-source platforme OCR moduli: Refaktoring modula i fix Odoo 16 OWL buga
Pozadina: zašto refaktorisati OCR modul?
Tokom razvoja AI/OCR obrade ulaznih faktura u Odoo 16, originalna implementacija bila je smještena u jedan modul — bill_draft_ocr_processing. Tokom vremena taj modul je prerastao u veliki paket koji je miješao nekoliko odgovornosti:
- LLM klijente i registry providera/modela
- Mapping pravila za polja fakture
- Wizard-e za obradu i konfiguraciju
- Per-user OCR podešavanja
Odluka je donesena da se sve podijeli u dvije jasno odijeljene cjeline:
| Novi modul | Odgovornost |
|---|---|
l10n_ba_ai_ocr_base | LLM provideri, modeli, mapping pravila, per-user podešavanja |
l10n_ba_ai_ocr_vendor_bill | account.move overrides, wizard-i, cron, processing log |
Ova podjela omogućava da l10n_ba_ai_ocr_base koriste i drugi moduli (npr. parseri bankovnih PDF-ova) bez potrebe da vuku cijelu logiku obrade faktura.
Migracija produkcijske baze
Procedura migracije na produkcijskom serveru (odoo-bringout-1):
# 1. Backup baze i filestore-a
python scripts/odoo-bringout-1.py --backup --with-filestore
# 2. Instalacija novih modula
python scripts/upgrade_production_nix_service.py \
--modules l10n_ba_ai_ocr_base,l10n_ba_ai_ocr_vendor_bill --install
# 3. Deinstalacija legacy modula
python scripts/upgrade_production_nix_service.py \
--modules bill_draft_ocr_processing --uninstall
Deinstalacija bill_draft_ocr_processing automatski je uklonila stare seed-data zapise (bill_ocr_provider, bill_ocr_model), pa su FK reference na res_users postavljene na NULL putem ON DELETE SET NULL. Mapping pravila (59 zapisa) su preživjela jer su to korisnički podaci, ne module data.
Per-user OCR podešavanja (provider, model) su restorirana direktnim SQL updateom koristeći backup kao referencu za stare ID-eve.
Bug: “Please pick the partner” — ali partner JE izabran
Nakon migracije, pri ručnom izboru partnera u wizard-u Configure OCR Rules, pojavio se error čim korisnik klikne “Create Partner Rule and Continue”:
Please pick the partner this rule should link to.

Vizualno, polje Link to Partner je prikazivalo izabranog partnera. No greška je i dalje ispaljivana.
Dijagnoza
Dodali smo WARNING logging u action_create_partner_rule koji je ispisivao i self.partner_id i sirovu DB vrijednost tog polja:
self.env.cr.execute(
"SELECT partner_id FROM configure_ocr_rules_wizard WHERE id=%s", (self.id,)
)
row = self.env.cr.fetchone()
_logger.warning(
"id=%s self.partner_id=%s db_partner_id=%s",
self.id,
self.partner_id.id if self.partner_id else None,
row[0] if row else "NO ROW",
)
Log je pokazao:
id=10 self.partner_id=None db_partner_id=None
Obje vrijednosti su bile None — što znači da korisnička izmjena nikad nije zapisana u bazu prije poziva dugmeta. Nije bilo niti jednog write() log unosa, bez obzira na izmjenu partnera u formi.
Uzrok: duplo polje u Odoo 16 OWL formi
Pregledom view XML-a pronašli smo da se polje partner_id pojavljuje dva puta u istoj formi:
<!-- Step 1 — editable -->
<group attrs="{'invisible': [('state','!=','partner')]}">
<field name="partner_id" options="{'no_create': True}"/>
</group>
<!-- Step 2 — readonly, invisible at load -->
<group attrs="{'invisible': [('state','!=','line')]}">
<field name="partner_id" readonly="1"/> <!-- ← PROBLEM -->
</group>
U Odoo 16 OWL reaktivnom sistemu, kada se isti field name pojavi više puta u istoj formi, framework registruje samo jedan widget kao kanonski za tracking izmjena. Budući da je Step 2 grupa nevidljiva pri učitavanju forme (state je inicijalno 'partner'), OWL je upravo taj — readonly, nevidljivi widget — registrovao kao “vlasnika” polja partner_id.
Rezultat: korisnikove izmjene u Step 1 editabilnom widgetu nikad nisu bile označene kao “dirty” u form modelu, pa nisu bile uključene u write() poziv prije izvršavanja dugmeta.

Fix
Rješenje je jednostavno — ukloniti partner_id iz Step 2 grupe. Informacija o partneru u tom koraku je već dostupna kroz created_partner_rule_id:
<!-- Step 2 — readonly partner_id UKLONJEN -->
<group attrs="{'invisible': [('state','!=','line')]}">
<field name="created_partner_rule_id" readonly="1"/>
<!-- partner_id više nije ovdje -->
</group>
Nakon deploymenta, write() se uredno poziva s partner_id vrijednošću prije nego što se izvrši akcija dugmeta.
Lekcija za Odoo 16 razvoj
Nikad ne staviti isti field name dva puta u istu Odoo 16 formu — posebno ne kombinaciju editable + readonly u grupama s
attrsinvisible. OWL registruje samo jedan widget po field imenu, i može izabrati “pogrešan”.
Ovo je posebno podmuklo jer forma vizualno izgleda ispravno — korisnik vidi izabranog partnera u polju — ali OWL interno ne prati tu izmjenu.
Napomena
Generisano od strane Claude 🤖
Ernad Husremović, hernad@bring.out.ba