Files
Odoo-Modules/fusion_accounting_ocr/views/account_move_views.xml
gsinghpal 125f48377a feat(fusion_accounting_ocr): pluggable OCR for vendor bills
Replaces Enterprise's account_invoice_extract with a Fusion-native pipeline:

Stage 1 (text extraction): Tesseract OCRs the bill attachment via
pytesseract + pdf2image. Pluggable OCRProvider adapter pattern allows
future Mindee / Google Document AI / Ollama-vision backends.

Stage 2 (field parsing): The fusion_accounting_ai LLMProvider reads the
raw OCR text and returns structured invoice fields (vendor, invoice
number, dates, amounts, line items) as JSON.

Draft invoice fields are auto-populated for empty-only fields (never
overwriting user-entered data). Vendor matching by name against
res.partner with supplier_rank > 0.

Adds:
- account.move.ocr_state (selection: not_requested/pending/processing/
  done/failed/manual)
- account.move.ocr_raw_text, ocr_extracted_data (Json), ocr_backend,
  ocr_confidence
- fusion.ocr.log (audit trail per OCR run)
- res.company.fusion_ocr_enabled / fusion_ocr_default_backend / auto_run
- /fusion/ocr/request_for_invoice JSON-RPC endpoint

Backend availability detected at runtime via OCRProvider.is_available()
classmethods. Tesseract 5.3.4 + pytesseract 0.3.13 + pdf2image 1.17.0
are installed in the container.

Tests: 13 (TesseractAdapter availability + image OCR; flow tests for
draft autofill, no-attachment guard, customer-invoice guard, ref-not-
overwritten; field parser empty/clean-json/markdown-fence/bad-JSON/
provider-exception). All pass on westin-v19 OrbStack VM.

Made-with: Cursor
2026-04-20 00:32:50 -04:00

46 lines
2.1 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_move_form_inherit_fusion_ocr" model="ir.ui.view">
<field name="name">account.move.form.inherit.fusion_ocr</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="action_request_ocr"
type="object"
string="Request OCR"
class="oe_highlight"
invisible="move_type not in ('in_invoice', 'in_refund') or ocr_state in ('processing', 'done')"/>
<button name="action_request_ocr"
type="object"
string="Re-run OCR"
invisible="move_type not in ('in_invoice', 'in_refund') or ocr_state not in ('done', 'failed', 'manual')"/>
</xpath>
<xpath expr="//sheet" position="inside">
<group string="Fusion OCR"
invisible="move_type not in ('in_invoice', 'in_refund') or ocr_state == 'not_requested'">
<group>
<field name="ocr_state" widget="badge"
decoration-success="ocr_state == 'done'"
decoration-info="ocr_state == 'processing'"
decoration-warning="ocr_state == 'manual'"
decoration-danger="ocr_state == 'failed'"/>
<field name="ocr_backend" readonly="1"/>
<field name="ocr_confidence" readonly="1" widget="percentage"/>
</group>
<group>
<field name="ocr_extracted_data" readonly="1" widget="text"/>
</group>
<field name="ocr_raw_text" readonly="1" nolabel="1"
placeholder="Raw OCR text..."/>
</group>
</xpath>
</field>
</record>
</odoo>