feat(import): Abgabeverträge (DOCX) auswerten und Tiere/Kontakte anreichern
All checks were successful
CI / Backend Tests (.NET) (push) Successful in 1m1s
CI / Frontend Tests (Node/Vite) (push) Successful in 9m35s
CI / Docker Build & Push (push) Successful in 1m18s

Neuer Parser extract_contracts.py liest die ~1,4k Abgabevertrags-DOCX
(\truenas\…\Verträge): er extrahiert aus dem Dokument-Body (zuverlässiger als
die Dateinamen) Käufer, Tier(e), Farbschlag, Abgabedatum und Preis — robust
gegen Word-Run-Splits (z. B. „F r au"/„3 0,00"); überspringt Vorlage,
Abstammungsnachweise und als .docx getarnte .doc.

enrich_from_contracts() in merge_and_resolve.py: Käufer werden als Kontakte
(IsReceiver) angelegt/zusammengeführt; Tiere werden KONSERVATIV per Rufname
(+ DOB-Jahr bei Mehrdeutigkeit) auf eigene Bestandstiere gematcht und erhalten
ReceiverContactId, GoHomeDate und Status „abgegeben" — nur wo nicht bereits
gesetzt; Konflikte werden geloggt, nicht überschrieben. Jede Übernahme bekommt
eine Herkunfts-Zeile („Abgabe an … aus Vertrag … übernommen.").

Ergebnis: 1095 Verträge → 783 Tier-Treffer (400 mehrdeutige übersprungen),
274 neue Abnehmer-Kontakte, 153 Tiere mit Abnehmer, 49 mit Abgabedatum,
23 neu „abgegeben". Keine Backend-/Frontend-Änderung nötig (Akte zeigt Abnehmer/
Abgabedatum/Herkunft bereits). SaleContract-Records bewusst nicht erzeugt
(bräuchte Migration + ingest-sichere Id — späterer Schritt).

Tests: test_extract_contracts.py (Dateiname/Body/Run-Split/Skip-Regeln) + alle
bestehenden grün; dotnet 212.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 16:53:47 +02:00
parent aa473b764e
commit 5e14124322
4 changed files with 849 additions and 3 deletions

View File

@@ -51,12 +51,25 @@ preserving everything: `genotype.mapped8locus`, `genotype.rawGenotype` (verbatim
```sh
cd tools/import
python extract.py # uses the default source paths
python extract.py # xlsx → animals.json / litters.json
python extract.py --stammbaeume "<dir>" --wurfchronik "<file.xlsx>"
python extract_docx.py # Wurfchronik-Detail.docx → docx_*.json
python extract_contracts.py # Abgabeverträge (.docx) → contracts.json
python merge_and_resolve.py # → resolved_import.json (DB-ready)
```
Requires Python 3. **Re-runnable / idempotent** — re-run when more files arrive
(Wurfchronik `Teil2+`, or new charts).
Requires Python 3 (zero third-party deps). **Re-runnable / idempotent** — re-run
when more files arrive (Wurfchronik `Teil2+`, new charts, or new contracts).
`extract_contracts.py` scans the breeder's sale-contract share
(`\\truenas\…\Verträge`, ~1.4k `.docx`) and emits one record per contract
(buyer, animal call-names, Farbschlag, dates, price, source filename). It skips
the blank template, `Abstammungsnachweis`/`Geburtsurkunde` documents, and any
file that is not a readable `.docx`. `merge_and_resolve.py` then conservatively
folds contracts into the resolved data: buyers become receiver `Contacts`, and
unambiguously matched gerbils get `ReceiverContactId` / `GoHomeDate` /
`Status=GivenAway` (only where not already set), with a provenance history line.
Ambiguous / unmatched animals are counted and skipped, never guessed.
## Output (`tools/import/output/`, git-ignored except the report)
@@ -64,6 +77,9 @@ Requires Python 3. **Re-runnable / idempotent** — re-run when more files arriv
|---|---|
| `animals.json` | deduped animals with genotype, parentRefs, photos, sourceFiles |
| `litters.json` | litters from the Wurfchronik |
| `docx_animals.json` / `docx_litters.json` | Wurfchronik-Detail.docx rows |
| `contracts.json` | one record per Abgabevertrag (buyer, animals, dates, price) |
| `resolved_import.json` | merged DB-ready payload consumed by `IngestResolvedService` |
| `photos/<slug>/…` | extracted, anchor-mapped images |
| `review-report.md` | **human review deliverable** (committed) |