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 modulOdgovornost
l10n_ba_ai_ocr_baseLLM provideri, modeli, mapping pravila, per-user podešavanja
l10n_ba_ai_ocr_vendor_billaccount.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.

Greška: partner je izabran ali validacija ne prolazi

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.

Link to Partner polje prazno nakon greške

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 attrs invisible. 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