File size: 2,355 Bytes
c1b1f3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from typing import Dict, Any, Tuple, List
import re
from datetime import datetime
from pydantic import BaseModel, Field

class ValidationResult(BaseModel):
    is_valid: bool
    errors: List[str] = Field(default_factory=list)
    confidence_penalty: float = 0.0

def validate_extracted_fields(doc_class: str, fields: Dict[str, Any]) -> ValidationResult:
    """
    Validates extracted field values based on business rules:
    - total_amount: numeric, > 0
    - date / invoice_date: valid date pattern
    - invoice_number: non-empty string
    Returns ValidationResult with pass/fail and penalty.
    """
    errors = []
    penalty = 0.0

    if doc_class in ["invoice", "receipt"]:
        amount_val = fields.get("total_amount")
        if amount_val is not None:
            # Clean currency symbols and commas
            cleaned_str = re.sub(r'[^\d.-]', '', str(amount_val).replace(',', ''))
            try:
                amt = float(cleaned_str)
                if amt <= 0:
                    errors.append(f"total_amount must be positive: got {amt}")
                    penalty += 0.25
            except ValueError:
                errors.append(f"total_amount cannot be parsed as float: {amount_val}")
                penalty += 0.35

    date_val = fields.get("date") or fields.get("invoice_date")
    if date_val is not None:
        # Check standard date formats
        valid_date = False
        date_str = str(date_val).strip()
        date_formats = ["%Y-%m-%d", "%d/%m/%Y", "%m/%d/%Y", "%d-%m-%Y", "%Y/%m/%d", "%b %d, %Y", "%d %b %Y"]
        for fmt in date_formats:
            try:
                datetime.strptime(date_str, fmt)
                valid_date = True
                break
            except ValueError:
                pass
        
        # Also check with date regex
        if not valid_date and not re.search(r'\d{1,4}[/\-.]\d{1,2}[/\-.]\d{1,4}', date_str):
            errors.append(f"date format unrecognized: {date_val}")
            penalty += 0.2

    if doc_class == "invoice":
        inv_num = fields.get("invoice_number")
        if inv_num is not None and not str(inv_num).strip():
            errors.append("invoice_number is empty")
            penalty += 0.3

    is_valid = len(errors) == 0
    return ValidationResult(is_valid=is_valid, errors=errors, confidence_penalty=min(penalty, 0.5))