Backend: Kaufpreis aus Beleg-PDF schätzen
guess_price_cents zieht den Kaufpreis aus dem Belegtext: bevorzugt den Betrag nach einem Summen-Stichwort (Gesamtbetrag/Total/…), sonst den groessten Betrag. Versteht Tausendertrenner (1'299.00) und deutsches Format (1.299,00). Der Upload liefert den Vorschlag als suggested_price_cents mit. 5 neue Tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -124,3 +124,56 @@ def guess_warranty_until(text: str, acquired_on: date | None = None) -> date | N
|
||||
if period and base:
|
||||
return _add_period(base, period[0], period[1])
|
||||
return _find_date_near_keyword(text)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Kaufpreis aus dem Beleg schätzen
|
||||
# --------------------------------------------------------------------------
|
||||
_TOTAL_KW = re.compile(
|
||||
r"gesamtbetrag|gesamtsumme|rechnungsbetrag|endbetrag|gesamt|summe|total|"
|
||||
r"zu zahlen|amount due|grand total|total due",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
# Beträge mit 2 Nachkommastellen, mit/ohne Tausendertrenner: 1'299.00 / 1.299,00 / 49,90
|
||||
_AMOUNT_RE = re.compile(r"\d{1,3}(?:[.\s']\d{3})+[.,]\d{2}|\d+[.,]\d{2}")
|
||||
|
||||
|
||||
def _amount_to_cents(raw: str) -> int | None:
|
||||
""""1'299.00" / "1.299,00" / "49,90" -> Rappen/Cent."""
|
||||
s = raw.replace(" ", "").replace("'", "")
|
||||
m = re.search(r"[.,](\d{2})$", s) # letzter Trenner ist der Dezimalpunkt
|
||||
if not m:
|
||||
return None
|
||||
dezimal = m.group(1)
|
||||
ganz = re.sub(r"[.,]", "", s[: m.start()]) # Tausendertrenner entfernen
|
||||
if not ganz.isdigit():
|
||||
return None
|
||||
return int(ganz) * 100 + int(dezimal)
|
||||
|
||||
|
||||
def guess_price_cents(text: str) -> int | None:
|
||||
"""Kaufpreis in Rappen/Cent aus dem Beleg oder ``None``.
|
||||
|
||||
Bevorzugt einen Betrag nahe einem Summen-Stichwort; sonst den größten Betrag
|
||||
(die Gesamtsumme ist auf Rechnungen meist der höchste Wert).
|
||||
"""
|
||||
if not text:
|
||||
return None
|
||||
betraege = [
|
||||
(m.start(), _amount_to_cents(m.group())) for m in _AMOUNT_RE.finditer(text)
|
||||
]
|
||||
betraege = [(pos, c) for pos, c in betraege if c]
|
||||
if not betraege:
|
||||
return None
|
||||
# Die Summe steht hinter ihrem Label ("Gesamtbetrag: 54.90") – deshalb den
|
||||
# nächsten Betrag *nach* dem Stichwort nehmen, nicht den absolut nächsten
|
||||
# (sonst gewönne eine davor stehende Zwischenzeile).
|
||||
best: int | None = None
|
||||
best_dist = 10**9
|
||||
for kw in _TOTAL_KW.finditer(text):
|
||||
for pos, c in betraege:
|
||||
if pos >= kw.start():
|
||||
dist = pos - kw.start()
|
||||
if dist <= 40 and dist < best_dist:
|
||||
best, best_dist = c, dist
|
||||
return best if best is not None else max(c for _, c in betraege)
|
||||
|
||||
Reference in New Issue
Block a user