Amazon-Kaufdatum erkennen; Preis-Vorschlaege beim Anlegen; Lagerort-Dropdowns mit Pfad

1) Beleganalyse erkennt Datumsangaben mit Monatsnamen (DE+EN, z.B. Amazon 'Bestelldatum 29 April 2026'), nicht nur 29.04.2026 - so wird das Kaufdatum solcher Rechnungen gefunden. 2) Beim direkten Anlegen eines Einzelstuecks mit Beleg werden jetzt alle erkannten Preise als Vorschlag angeboten (Dropdown), statt stillschweigend nur den wahrscheinlichsten zu nehmen. 3) Alle Lagerort-Dropdowns zeigen den vollen Pfad (Hedingen -> Keller -> Schublade 1) statt nur den Namen, damit gleichnamige Orte unterscheidbar sind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-27 13:24:08 +02:00
parent 21ab0a825a
commit c8c1686395
7 changed files with 114 additions and 19 deletions

View File

@@ -25,9 +25,33 @@ _PURCHASE_KW = re.compile(
r"datum|invoice|order date|purchase|receipt",
re.IGNORECASE,
)
# 2024-03-31 oder 31.03.2024 / 31/03/24
# Monatsnamen (Deutsch + Englisch, mit gängigen Abkürzungen), damit z.B.
# Amazon-Rechnungen mit „29 April 2026" erkannt werden nicht nur 29.04.2026.
_MONTHS = {
"januar": 1, "jan": 1, "january": 1,
"februar": 2, "feb": 2, "february": 2,
"märz": 3, "maerz": 3, "mrz": 3, "mär": 3, "mar": 3, "march": 3,
"april": 4, "apr": 4,
"mai": 5, "may": 5,
"juni": 6, "jun": 6, "june": 6,
"juli": 7, "jul": 7, "july": 7,
"august": 8, "aug": 8,
"september": 9, "sept": 9, "sep": 9,
"oktober": 10, "okt": 10, "october": 10, "oct": 10,
"november": 11, "nov": 11,
"dezember": 12, "dez": 12, "december": 12, "dec": 12,
}
# Längere Namen zuerst, damit „januar" vor „jan" greift.
_MONTH_ALT = "|".join(sorted((re.escape(k) for k in _MONTHS), key=len, reverse=True))
# ISO 2024-03-31, numerisch 31.03.2024 / 31/03/24, oder mit Monatsname
# ("29 April 2026" bzw. "April 29, 2026").
_DATE_RE = re.compile(
r"(\d{4})-(\d{1,2})-(\d{1,2})|(\d{1,2})[.\/](\d{1,2})[.\/](\d{2,4})"
r"(?P<iy>\d{4})-(?P<im>\d{1,2})-(?P<idd>\d{1,2})"
r"|(?P<d>\d{1,2})[.\/](?P<m>\d{1,2})[.\/](?P<y>\d{2,4})"
r"|(?P<nd>\d{1,2})\.?\s+(?P<nmon>" + _MONTH_ALT + r")\.?\s+(?P<ny>\d{4})"
r"|(?P<emon>" + _MONTH_ALT + r")\s+(?P<ed>\d{1,2})(?:th|st|nd|rd)?,?\s+(?P<ey>\d{4})",
re.IGNORECASE,
)
_WINDOW = 60 # Zeichen um ein Stichwort, in denen Zahl/Datum als zugehörig gelten
@@ -45,16 +69,22 @@ def extract_pdf_text(data: bytes) -> str:
def _match_to_date(m: re.Match) -> date | None:
g = m.groups()
g = m.groupdict()
try:
if g[0] is not None: # ISO yyyy-mm-dd
y, mo, d = int(g[0]), int(g[1]), int(g[2])
else: # dd.mm.yyyy / dd/mm/yy
d, mo, y = int(g[3]), int(g[4]), int(g[5])
if g.get("iy"): # ISO yyyy-mm-dd
y, mo, d = int(g["iy"]), int(g["im"]), int(g["idd"])
elif g.get("d"): # dd.mm.yyyy / dd/mm/yy
d, mo, y = int(g["d"]), int(g["m"]), int(g["y"])
if y < 100:
y += 2000
elif g.get("nd"): # 29 April 2026
d, mo, y = int(g["nd"]), _MONTHS[g["nmon"].lower()], int(g["ny"])
elif g.get("emon"): # April 29, 2026
d, mo, y = int(g["ed"]), _MONTHS[g["emon"].lower()], int(g["ey"])
else:
return None
return date(y, mo, d)
except (TypeError, ValueError):
except (TypeError, ValueError, KeyError):
return None