"""Regression tests from real-world invoices (anonymized). Every test in this module originated from a production bug. When a new real invoice reveals a service bug: anonymize the invoice XML into tests/fixtures/real/, verify it contains no real-world data, and add a test here that locks in the correct behavior. """ from io import BytesIO from pathlib import Path import pytest from pypdf import PdfWriter from src.extractor import extract_zugferd from src.models import ValidateRequest from src.validator import validate_invoice REAL_FIXTURES = Path(__file__).parent / "fixtures" / "real" ALL_CHECKS = ["pflichtfelder", "betraege", "ustid", "pdf_abgleich"] def _pdf_with_attachment(xml_bytes: bytes) -> bytes: """Wrap invoice XML in a minimal PDF (no visual content).""" writer = PdfWriter() writer.add_blank_page(width=72, height=72) writer.add_attachment("factur-x.xml", xml_bytes) output = BytesIO() writer.write(output) return output.getvalue() class TestFcTaxNumberBasisQuantityInvoice: """Anonymized EXTENDED invoice: FC-only tax registration (no VAT ID), unit price with basis quantity 100, spaced buyer VAT ID, seller email in DefinedTradeContact. Regressions locked in (found 2026-09 on a real workshop invoice): supplier tax_number never extracted, unit price off by factor 100, spaced VAT ID rejected as invalid format, contact email dropped. """ XML_PATH = REAL_FIXTURES / "extended_fc_steuernummer_basisqty.xml" def test_extraction(self) -> None: pdf_bytes = _pdf_with_attachment(self.XML_PATH.read_bytes()) result = extract_zugferd(pdf_bytes) assert result.is_zugferd is True data = result.xml_data assert data.invoice_number == "4711" assert data.supplier.tax_number == "1112223334444" assert data.supplier.vat_id is None assert data.supplier.email == "rechnung@beispielwerkstatt.example" assert data.buyer.vat_id == "DE 123 456 789" assert len(data.line_items) == 2 first, second = data.line_items assert first.quantity == 140 assert first.unit_price == pytest.approx(0.7585) assert first.line_total == pytest.approx(106.19) assert second.quantity == 50 assert second.unit_price == pytest.approx(0.7585) assert second.line_total == pytest.approx(37.93) assert data.totals.net == pytest.approx(144.12) assert data.totals.vat_total == pytest.approx(10.09) assert data.totals.gross == pytest.approx(154.21) def test_validation_all_checks_pass(self) -> None: pdf_bytes = _pdf_with_attachment(self.XML_PATH.read_bytes()) result = extract_zugferd(pdf_bytes) request = ValidateRequest( xml_data=result.xml_data.model_dump(), pdf_text=result.pdf_text, checks=ALL_CHECKS, ) validation = validate_invoice(request) assert validation.is_valid is True assert validation.errors == []