Add review stage coverage and fix XML tax deduplication

- Reviewer now sends all pages to the review VLM alongside extracted JSON.

- XML builder deduplicates account.tax records by rate.

- XML builder raises on confidence below min_confidence_threshold.

- Adds fractional tax-rate support with stable external IDs.

- Adds expected_invoice.xml fixture, reviewer tests, and XML regression tests.
This commit is contained in:
2026-08-21 16:44:25 +02:00
parent 570c8585a0
commit e2becd6219
6 changed files with 270 additions and 45 deletions
+85
View File
@@ -0,0 +1,85 @@
"""Tests for the vision review stage."""
from __future__ import annotations
import pytest
from PIL import Image
from odoo_ocr.config import Settings
from odoo_ocr.pipeline.reviewer import review_invoice
from odoo_ocr.schemas import ExtractedInvoice, ReviewResult
from tests.conftest import FakeVLMClient
def make_invoice() -> ExtractedInvoice:
return ExtractedInvoice(
vendor_name="Acme",
invoice_number="INV-1",
invoice_date="2024-01-01",
)
@pytest.mark.asyncio
async def test_review_returns_parsed_result() -> None:
client = FakeVLMClient(
responses={
"invoice review assistant": {
"valid": True,
"confidence": 0.95,
"issues": [],
"corrected_invoice": None,
}
}
)
result = await review_invoice(
make_invoice(), [Image.new("RGB", (10, 10))], client, Settings()
)
assert isinstance(result, ReviewResult)
assert result.valid is True
assert result.confidence == 0.95
@pytest.mark.asyncio
async def test_review_sends_all_pages() -> None:
client = FakeVLMClient(
responses={
"invoice review assistant": {
"valid": True,
"confidence": 0.90,
"issues": [],
"corrected_invoice": None,
}
}
)
pages = [Image.new("RGB", (10, 10)) for _ in range(3)]
await review_invoice(make_invoice(), pages, client, Settings())
assert client.calls[-1]["image_count"] == len(pages)
@pytest.mark.asyncio
async def test_review_preserves_corrected_invoice() -> None:
corrected = {
"vendor_name": "Acme",
"invoice_number": "INV-1",
"invoice_date": "2024-01-01",
"line_items": [],
"subtotal": 100.0,
"tax_total": 0.0,
"total": 100.0,
}
client = FakeVLMClient(
responses={
"invoice review assistant": {
"valid": True,
"confidence": 0.95,
"issues": [{"field": "total", "severity": "warning", "message": "rounded"}],
"corrected_invoice": corrected,
}
}
)
result = await review_invoice(
make_invoice(), [Image.new("RGB", (10, 10))], client, Settings()
)
assert result.corrected_invoice is not None
assert result.corrected_invoice.total == 100.0
assert result.issues[0].severity == "warning"