feat: Add multiple TVA entries support for Romanian receipts
- Add TvaEntry schema supporting multiple TVA rates (A, B, C, D codes) - Update OCR extractor to extract multiple TVA entries from receipts - Support both old (19%, 9%, 5%) and new Romanian rates (21%, 11% from Aug 2025) - Add tva_breakdown, tva_total, items_count, vendor_address to Receipt model - Update OCRPreview.vue to display TVA entries with rate badges - Add "Detalii Suplimentare" section in ReceiptCreateView with editable TVA table - Add TVA breakdown display in ReceiptDetailView - Create database migration for new TVA columns 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,18 @@
|
||||
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TvaEntry(BaseModel):
|
||||
"""Single TVA entry with code, percentage and amount."""
|
||||
code: Optional[str] = Field(default=None, description="TVA code: A, B, C, D")
|
||||
percent: int = Field(description="TVA percentage: 0, 5, 9, 19, 21")
|
||||
amount: Decimal = Field(description="TVA amount for this rate")
|
||||
|
||||
|
||||
class ExtractionData(BaseModel):
|
||||
"""Extracted receipt data from OCR."""
|
||||
|
||||
@@ -19,6 +26,12 @@ class ExtractionData(BaseModel):
|
||||
cui: Optional[str] = Field(default=None, description="CUI (fiscal identification code)")
|
||||
description: Optional[str] = Field(default=None, description="Optional description")
|
||||
|
||||
# Additional extracted fields - Multiple TVA entries support
|
||||
tva_entries: List[TvaEntry] = Field(default=[], description="List of TVA entries by rate (A, B, C, D)")
|
||||
tva_total: Optional[Decimal] = Field(default=None, description="Total TVA amount")
|
||||
address: Optional[str] = Field(default=None, description="Vendor address")
|
||||
items_count: Optional[int] = Field(default=None, description="Number of items/articles")
|
||||
|
||||
confidence_amount: float = Field(default=0.0, ge=0, le=1, description="Amount extraction confidence")
|
||||
confidence_date: float = Field(default=0.0, ge=0, le=1, description="Date extraction confidence")
|
||||
confidence_vendor: float = Field(default=0.0, ge=0, le=1, description="Vendor extraction confidence")
|
||||
@@ -30,18 +43,25 @@ class ExtractionData(BaseModel):
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"receipt_type": "bon_fiscal",
|
||||
"receipt_number": "12345",
|
||||
"receipt_series": None,
|
||||
"receipt_date": "2024-01-15",
|
||||
"amount": 125.50,
|
||||
"partner_name": "MEGA IMAGE SRL",
|
||||
"cui": "12345678",
|
||||
"receipt_number": "1360760",
|
||||
"receipt_series": "0146",
|
||||
"receipt_date": "2025-10-11",
|
||||
"amount": 186.16,
|
||||
"partner_name": "FIVE-HOLDING S.A.",
|
||||
"cui": "10562600",
|
||||
"description": None,
|
||||
"confidence_amount": 0.95,
|
||||
"confidence_date": 0.90,
|
||||
"confidence_vendor": 0.75,
|
||||
"overall_confidence": 0.87,
|
||||
"raw_text": "BON FISCAL\nMEGA IMAGE SRL\n..."
|
||||
"tva_entries": [
|
||||
{"code": "A", "percent": 19, "amount": 25.00},
|
||||
{"code": "B", "percent": 9, "amount": 7.31}
|
||||
],
|
||||
"tva_total": 32.31,
|
||||
"address": "JUD. CONSTANTA, MUN. CONSTANTA, STR. ION ROATA NR. 3",
|
||||
"items_count": 17,
|
||||
"confidence_amount": 0.98,
|
||||
"confidence_date": 0.98,
|
||||
"confidence_vendor": 0.95,
|
||||
"overall_confidence": 0.97,
|
||||
"raw_text": "FIVE-HOLDING S.A.\nCIF: RO10562600\n..."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,15 @@ class AttachmentResponse(BaseModel):
|
||||
uploaded_at: datetime
|
||||
|
||||
|
||||
# ============ TVA Schema ============
|
||||
|
||||
class TvaEntrySchema(BaseModel):
|
||||
"""Single TVA entry with code, percentage and amount."""
|
||||
code: Optional[str] = Field(default=None, description="TVA code: A, B, C, D")
|
||||
percent: int = Field(description="TVA percentage: 0, 5, 9, 19, 21")
|
||||
amount: Decimal = Field(description="TVA amount for this rate")
|
||||
|
||||
|
||||
# ============ Receipt Schemas ============
|
||||
|
||||
class ReceiptBase(BaseModel):
|
||||
@@ -75,6 +84,12 @@ class ReceiptBase(BaseModel):
|
||||
receipt_date: date
|
||||
amount: Decimal = Field(gt=0)
|
||||
description: Optional[str] = Field(default=None, max_length=500)
|
||||
# TVA info (multiple entries support)
|
||||
tva_breakdown: Optional[List[TvaEntrySchema]] = Field(default=None, description="List of TVA entries")
|
||||
tva_total: Optional[Decimal] = Field(default=None, description="Total TVA amount")
|
||||
items_count: Optional[int] = Field(default=None, description="Number of items")
|
||||
vendor_address: Optional[str] = Field(default=None, max_length=500, description="Vendor address")
|
||||
# Other fields
|
||||
expense_type_code: Optional[str] = Field(default=None, max_length=20)
|
||||
company_id: int
|
||||
partner_id: Optional[int] = None
|
||||
@@ -98,6 +113,12 @@ class ReceiptUpdate(BaseModel):
|
||||
receipt_date: Optional[date] = None
|
||||
amount: Optional[Decimal] = Field(default=None, gt=0)
|
||||
description: Optional[str] = Field(default=None, max_length=500)
|
||||
# TVA info (multiple entries support)
|
||||
tva_breakdown: Optional[List[TvaEntrySchema]] = Field(default=None, description="List of TVA entries")
|
||||
tva_total: Optional[Decimal] = Field(default=None, description="Total TVA amount")
|
||||
items_count: Optional[int] = Field(default=None, description="Number of items")
|
||||
vendor_address: Optional[str] = Field(default=None, max_length=500, description="Vendor address")
|
||||
# Other fields
|
||||
expense_type_code: Optional[str] = Field(default=None, max_length=20)
|
||||
partner_id: Optional[int] = None
|
||||
partner_name: Optional[str] = Field(default=None, max_length=200)
|
||||
|
||||
Reference in New Issue
Block a user