17 iteracija do savršenstva: Razvoj Odoo fiskalizacije uz Claude AI


Uvod

Kako izgleda razvoj softvera uz pomoć AI asistenta? Ova priča opisuje realnu razvojnu sesiju u kojoj smo implementirali novu funkcionalnost u našem Odoo modulu za bosanskohercegovačku fiskalizaciju - automatsku generaciju PDF-a nakon uspješne fiskalizacije računa.

Rezultat: 17 verzija, 2 sata rada, 9 izmijenjenih fajlova i mnoštvo naučenih lekcija.

Kompletan izvještaj sesije: GitHub Gist

Funkcionalnost u akciji

Automatska generacija PDF-a nakon fiskalizacije

Animirani prikaz automatskog generisanja PDF-a odmah nakon uspješne fiskalizacije računa

Početni zahtjev

Zahtjev je postavio Jasko na našem Nextcloud Talk-u:

Znaš štaš’ napravit, kod fakturisanja čim završi EDI da i PDF sam izgeneriše

Ernad je to formulisao Claude-u kao sljedeći zahtjev:

Process Now activate fiscalization, after success we receive fiscal number. After that step print invoice to PDF.

Option: This should be available as user option. When checked - generate PDF after fiscalization. Option name: Generate PDF after fiscalization default false

Razvojna avantura kroz verzije

Faza 1: Pogrešan pristup (verzije 16.0.2.6.5 → 16.0.2.6.6)

Prva implementacija je koristila EDI server endpoint za dobijanje PDF-a:

# POGREŠNO: Koristi EDI server PDF (samo za OFS driver)
if user_for_config.l10n_ba_edi_generate_pdf_after_fiscalization:
    pdf_data = invoice._get_edi_pdf_from_server()  # ❌

Problem: Korisnik je želio standardni Odoo PDF računa, a ne PDF sa EDI servera.

Faza 2: Prelazak na standardni PDF (verzije 16.0.2.6.7 → 16.0.2.6.10)

Pokušaj korištenja Odoo standardnog izvještaja:

# Pokušaj korištenja standardnog Odoo izvještaja
report = self.env.ref('account.account_invoices')
pdf_content, _ = report._render_qweb_pdf(invoice.ids)  # ❌ Pogrešan potpis

Greška: 'list' object has no attribute 'split'

Uzrok: Metoda _render_qweb_pdf() očekuje report_ref kao prvi parametar, a mi smo proslijedili listu ID-eva kao pozicioni argument.

Faza 3: Problem duplikata (verzije 16.0.2.6.11 → 16.0.2.6.16)

Nakon ispravke potpisa metode, pojavio se novi problem - kreiranje duplikata PDF-a.

Pokušaji rješenja:

  1. Provjera postojećih PDF-a prije generisanja
  2. Korištenje action_invoice_print() - vraća samo action dict, ne kreira attachment
  3. Korištenje report.report_action() - isti problem
  4. Dodavanje detaljnog logovanja
# Provjera postojećih PDF-a
existing_pdf = self.env["ir.attachment"].search([
    ("res_model", "=", "account.move"),
    ("res_id", "=", invoice.id),
    ("mimetype", "=", "application/pdf"),
], limit=1)

Problem: I dalje duplikati!

Faza 4: Konsultacija sa GPT-5 (verzija 16.0.2.6.17)

Pitanje: Kako generisati PDF računa u Odoo 16 bez duplikata?

GPT-5 odgovor:

  • _render_qweb_pdf() NE kreira automatski attachmente
  • Automatsko kreiranje se dešava putem _render_qweb_pdf_prepare_streams()
  • Potrebno manuelno kreiranje attachmenta
  • Koristiti datas field sa base64 enkodingom umjesto raw

Preporučeno rješenje:

report = self.env.ref('account.account_invoices')
pdf_content, _ = report._render_qweb_pdf('account.account_invoices', res_ids=invoice.ids)

pdf_name = safe_eval(report.print_report_name, {'object': invoice, 'time': time})
attachment = self.env["ir.attachment"].create({
    "name": pdf_name,
    "type": "binary",
    "datas": base64.b64encode(pdf_content),
    "res_model": "account.move",
    "res_id": invoice.id,
    "mimetype": "application/pdf",
})

Faza 5: Konačno otkriće (verzije 16.0.2.6.18 → 16.0.2.6.22)

Kritična analiza logova:

2025-11-07 12:31:36,861 INFO The PDF documents 'INV_2025_00229.pdf' are now saved in the database
2025-11-07 12:31:36,862 INFO Invoice PDF created: INV_2025_00229.pdf, ID: 620

Otkrovenje:

  • _render_qweb_pdf() KREIRA automatski attachmente (suprotno GPT-5 tvrdnji)!
  • U Odoo 16, metoda poziva _render_qweb_pdf_prepare_streams() koja kreira attachmente
  • Manuelno kreiranje je uzrokovalo duplikate!

Konačno rješenje:

# Provjeri da li korisnik želi generaciju PDF-a
if user_for_config.l10n_ba_edi_generate_pdf_after_fiscalization:
    # 1. Provjeri da li PDF već postoji
    existing_pdf = self.env["ir.attachment"].search([
        ("res_model", "=", "account.move"),
        ("res_id", "=", invoice.id),
        ("mimetype", "=", "application/pdf"),
    ], limit=1)

    if not existing_pdf:
        # 2. Pozovi _render_qweb_pdf (automatski kreira attachment)
        report = self.env.ref('account.account_invoices')
        report._render_qweb_pdf('account.account_invoices', res_ids=invoice.ids)

        # 3. Verifikuj da je kreiran (samo za logging)
        created_pdf = self.env["ir.attachment"].search([
            ("res_model", "=", "account.move"),
            ("res_id", "=", invoice.id),
            ("mimetype", "=", "application/pdf"),
        ], limit=1, order="id desc")

        if created_pdf:
            _logger.info(f"PDF auto-kreiran: {created_pdf.name}, ID: {created_pdf.id}")

Izmijenjeni fajlovi

Core implementacija

  1. account_edi_format.py - Glavna logika PDF generacije
  2. res_users.py - Polje za korisničku opciju
  3. res_config_settings.py - Integracija u Settings

Korisnički interfejs

  1. res_config_settings_views.xml - Settings checkbox
  2. user_fiscal_config_wizard.py - Wizard model
  3. user_fiscal_config_wizard_views.xml - Wizard UI

Verzije

  1. manifest.py - Verzija: 16.0.2.6.5 → 16.0.2.6.22
  2. pyproject.toml - Sinhronizovana verzija

Ključne lekcije

Tehničke spoznaje

  1. Odoo Report Rendering:

    • _render_qweb_pdf(report_ref, res_ids=None, data=None) - Glavna metoda
    • Interno poziva _render_qweb_pdf_prepare_streams() koja KREIRA attachmente
    • Automatsko kreiranje se dešava kada je report pravilno konfigurisan
  2. Attachment kreiranje:

    • U Odoo 16, _render_qweb_pdf() automatski kreira attachmente
    • Manuelno kreiranje nakon renderinga uzrokuje duplikate
    • Provjera postojećih attachmenata sprječava duplikate
  3. User kontekst u EDI:

    • EDI procesiranje često radi kao system user (__system__)
    • Mora se izvući stvarni korisnik iz računa: invoice.invoice_user_id ili invoice.create_uid
    • Koristiti postavke korisnika sa računa, ne sistemskog korisnika

Istorija verzija

VerzijaPromjenaProblem
16.0.2.6.6Inicijalna implementacija sa EDI server PDFPogrešan pristup
16.0.2.6.7Prelazak na standardni Odoo PDFGreška u potpisu
16.0.2.6.8-10Ispravka potpisa metodeI dalje greške
16.0.2.6.11-16Različiti pristupi protiv duplikataDuplikati i dalje
16.0.2.6.17Konsultacija sa GPT-5Pogrešan savjet
16.0.2.6.18-21Implementacija GPT-5 rješenjaDuplikati!
16.0.2.6.22Uklanjanje manuelnog kreiranjaUSPJEH!

Statistika

  • Ukupno verzija: 17 iteracija
  • Izmijenjenih fajlova: 9
  • Dodato linija: 86
  • Uklonjeno linija: 3
  • Utrošeno vrijeme: ~2 sata
  • Glavni izazov: Razumijevanje Odoo automatskog kreiranja attachmenata

Test checklist

  • ✅ Opcija se pojavljuje u Settings za admin korisnike
  • ✅ Opcija se pojavljuje u User Fiscal Config Wizard za obične korisnike
  • ✅ PDF se generiše kada je opcija uključena i račun fiskalizovan
  • ✅ PDF se ne generiše kada je opcija isključena
  • ✅ Nema duplikata PDF-a
  • ✅ Detekcija postojećih PDF-a radi
  • ✅ Fiskalizacija uspijeva čak i ako generisanje PDF-a ne uspije
  • ✅ Radi sa oba FPrint i OFS drajverima
  • ✅ Database polje pravilno čuva korisničke postavke
  • ✅ PDF ima pravilan naziv iz report template-a

Zaključak

Nakon 17 iteracija i ispitivanja više pristupa, rješenje je bilo iznenadjujuće jednostavno: pustiti Odoo-ovu _render_qweb_pdf() metodu da obavi svoj posao automatski.

Glavni izazov je bio razumijevanje da u Odoo 16 ova metoda KREIRA attachmente automatski, suprotno nekoj dokumentaciji i čak i GPT-5 inicijalnom uputstvu.

Ključna spoznaja je došla iz pažljive analize logova koja je pokazala automatsko kreiranje attachmenta prije našeg pokušaja manuelnog kreiranja. Nakon što smo uklonili manuelno kreiranje i pustili Odoo da to uradi, sve je radilo savršeno.

Lekcija o razvoju sa AI

Ova priča ilustruje važan aspekt razvoja softvera uz pomoć AI asistenata:

  1. AI nije uvijek u pravu - Čak i GPT-5 može dati netačne informacije
  2. Analiza logova je ključna - Stvarna istina se nalazi u logu, ne u dokumentaciji
  3. Iterativni razvoj funkcioniše - 17 pokušaja nije neuspjeh, to je proces učenja
  4. Jednostavnost pobjeđuje - Najbolje rješenje je često najjednostavnije

Alati koje koristimo

Tokom razvoja ove funkcionalnosti koristili smo:

  • Claude Code - AI asistent za razvoj (Sonnet 4.5)
  • VS Code - Glavni razvojni editor
  • Odoo 16 - ERP platforma
  • Python 3 - Programski jezik
  • PostgreSQL - Baza podataka
  • Git - Verzioniranje koda
  • FFmpeg - Kreiranje GIF-a za dokumentaciju

Status: ✅ Funkcionalnost implementirana, testirana i puštena u produkciju.


Claude sesije

Tokom razvoja ove funkcionalnosti, Claude Code nije samo pomogao u implementaciji koda, već i u pripremi dokumentacije.

Claude Code u akciji

Primjer Claude Code sesije - kreiranje animiranog GIF-a za dokumentaciju funkcionalnosti

Ovaj screenshot prikazuje kako smo koristili Claude Code da automatski kreira animirani GIF iz snimljenog MP4 video fajla koristeći FFmpeg. Claude je:

  • Provjerio postojanje fajla
  • Kreirao optimizovanu FFmpeg komandu
  • Generisao GIF sa smanjenom rezolucijom (800px širina) i framerate-om (10fps)
  • Koristio custom paletu za bolje boje
  • Smanjio veličinu sa 1.7MB na 762KB

Širi kontekst: AI alati i bring.out

Ovaj blog post je dio veće priče o tome kako su AI alati transformisali naš način razvoja softvera.

👉 Pročitajte cijelu priču: Kako smo postali najveći proizvođači software-a na Odoo open-source platformi u Bosni i Hercegovini

U tom postu možete saznati:

  • Kako smo otkrili AI alate preko slučajnog razgovora
  • Zašto je terminal pravo mjesto za AI kolaboraciju
  • Priču o “Džemu” - našem prvom AI agentu
  • Kako smo završili projekat 15 dana prije roka
  • Šta AI alati znače za našu poziciju na Odoo tržištu u BiH

Napomene

Ovaj sadržaj je napisao claude.ai 🤖 prema Ernadovim uputama.


Reference