236053cd7f
Kategoriserer danske regnskabs-/ERP-skills, ERP-data-afstemnings-
skill og to devops-workflows (gitea-issue-agent, kanban-workflows)
som Radix-medlemmers AI-agenter kan dele.
Struktur:
skills/<kategori>/<skill-navn>/SKILL.md
skills/<kategori>/<skill-navn>/{references,templates,scripts}/
Indekseret af index.json (genereret af scripts/rebuild_index.py).
Kør `python3 scripts/rebuild_index.py --check` i CI for at fange
synkroniseringsfejl.
236 lines
8.5 KiB
Markdown
236 lines
8.5 KiB
Markdown
# Coop ↔ Uniconta Reconciliation Reference (Aktualiseret 2025-05-26)
|
|
|
|
Konkrete felter, mappings og pitfalls fra det aktuelle Coop-Uniconta projekt.
|
|
|
|
---
|
|
|
|
## Coop (SAP export) — Kontoudtog.xlsx
|
|
|
|
| Kolonne | Navn | Type | Match-brug |
|
|
|---------|------|------|------------|
|
|
| Entry Date | Entry Date | datetime | Sekundær |
|
|
| Document Type | Document Type | **string** | **Filter + match-type** |
|
|
| Document Date | Document Date | datetime | Sekundær |
|
|
| **Posting Date** | Posting Date | **datetime** | **Primær dato** |
|
|
| **Reference** | Reference | **string (75% non-null)** | **Primær match-nøgle** |
|
|
| Net Due Date | Net Due Date | datetime | Forfaldsdato |
|
|
| **Amount** | Amount in Local Currency | **float** | **Primær beløb** |
|
|
| Document Currency | Local Currency | string ('DKK') | Filter |
|
|
| **Document Number** | Document Number | int | **Audit/trace** |
|
|
| Text | Text | string (20% non-null) | Sekundær match |
|
|
| Payment Reference | Payment Reference | string (12% non-null) | OCR/trace |
|
|
| User Name | User Name | string | Audit |
|
|
|
|
### Coop Document Types (2024-2025)
|
|
| Type | Andel | Beskrivelse | Uniconta-modpart |
|
|
|------|-------|-------------|----------------|
|
|
| RE | ~50% | Regning/betaling | Betaling/Faktura |
|
|
| ZV | ~15% | Udbetalingsforslag/faktura | Betaling |
|
|
| RG | ~6% | Kontoafstemning | Kreditnota |
|
|
| KG | ~5% | Kontoafstemning gruppe (bonus/rabat) | Kreditnota |
|
|
| ZC | ~3% | Kreditering | Kreditnota |
|
|
| ZP | ~0.4% | Betaling | Betaling |
|
|
| XX | ~0% (filtreret fra) | Manuelt bogført/migrering | — |
|
|
|
|
**VIGTIGT:** ZV-poster er positive fakturaer udstedt til kunder. De har typisk **ingen Reference/Faktura-nummer** og matches via `abs(beløb) + dato` mod Uniconta Betalinger.
|
|
|
|
---
|
|
|
|
## Uniconta — Coop danmark.xlsx
|
|
|
|
### Fil-struktur
|
|
- Række 1: Konto-info (Checked, Konto, Kontonavn, Adresse, Bynavn)
|
|
- Række 2-3: Tomme
|
|
- Række 4: Kolonne-headers: Dato, Faktura, Forfaldsdato, Bilag, Tekst, Beløb, Resterende, Forfalden, Konteringstype, Sum
|
|
- Række 5+: Data
|
|
|
|
| Kolonne | Navn | Type | Match-brug |
|
|
|---------|------|------|------------|
|
|
| **Dato** | Dato | **datetime** | **Primær dato** |
|
|
| **Faktura** | Faktura | **mixed int/str, 0=ingen** | **Primær match-nøgle** |
|
|
| Forfaldsdato | Forfaldsdato | datetime | Sekundær |
|
|
| Bilag | Bilag | mixed int/str | Audit |
|
|
| **Tekst** | Tekst | **string** | **Tekst-match** |
|
|
| **Beløb** | Beløb | **float/tekst** | **Primær beløb** |
|
|
| Resterende | Resterende | float | Filter |
|
|
| Forfalden | Forfalden | float | Filter |
|
|
| **Konteringstype** | Konteringstype | **string** | **Filter + match-bonus** |
|
|
| Sum | Sum | float | IGNORE — løbende saldo |
|
|
|
|
### Uniconta Konteringstyper
|
|
| Type | Beskrivelse | Match-modpart (Coop) |
|
|
|------|-------------|---------------------|
|
|
| Faktura | Faktura udstedt | RE (betaling) eller ZV |
|
|
| Betaling | Betaling modtaget | RE, ZP, ZV |
|
|
| Kreditnota | Kreditnota | RG, ZC, KG |
|
|
| Primo | Primo-saldo | XX (filtreres fra) |
|
|
| Afslutning | Årsafslutning | — |
|
|
| Overførsel | Overførselsbilag | — |
|
|
| Manuel | Manuel posting | — |
|
|
| Rabat | Rabat | KG |
|
|
|
|
---
|
|
|
|
## Match-nøgle-observationer
|
|
|
|
### Fakturanumre overlapper IDENTISK
|
|
- Coop `Reference` og Uniconta `Faktura` bruger **identiske 4-5 cifrede tal**.
|
|
- Eksempler: 46344, 44907, 48456, 49495, 51519
|
|
- **2.970 unikke RE-references findes som Faktura-numre i Uniconta.**
|
|
- Dette er den stærkeste match-signal — langt stærkere end beløb+dato alene.
|
|
|
|
### Modsat fortegn er NORMALT
|
|
| Coop type | Coop fortegn | Uniconta type | Uniconta fortegn | Match |
|
|
|-----------|-------------|---------------|-----------------|-------|
|
|
| RE (betaling) | Negativ (-) | Faktura | Positiv (+) | abs(beløb) |
|
|
| ZV (faktura) | Positiv (+) | Betaling | Negativ (-) | abs(beløb) |
|
|
| RG (kreditnota) | Positiv (+) | Kreditnota | Negativ (-) | abs(beløb) |
|
|
| KG (rabat) | Positiv (+) | Kreditnota | Negativ (-) | abs(beløb) |
|
|
|
|
**Implikation:** Match-motoren SKAL sammenligne `abs(beløb)`, ikke rå beløb.
|
|
|
|
### Dato-tolerance
|
|
- Faktura-reference matches: ±21 dage fungerer fint
|
|
- Generelle beløb+dato matches: ±21 dage fanger de fleste
|
|
- ZV↔Betaling matches: ±30 dage anbefales (betalingsforslag vs. faktura)
|
|
|
|
---
|
|
|
|
## Normalisering
|
|
|
|
### Beløb (dansk format)
|
|
Nogle Uniconta-celler indeholder tekststrenge som `-2.543.803,93`:
|
|
```python
|
|
def parse_danish_number(val):
|
|
if pd.isna(val) or val is None: return None
|
|
if isinstance(val, (int, float)): return float(val)
|
|
s = str(val).strip().replace('\xa0','').replace('\u202f','').replace(' DKK','')
|
|
if s.lower() in ('nan',''): return None
|
|
if s.endswith('.0'): s = s[:-2]
|
|
if '.' in s and ',' in s:
|
|
s = s.replace('.','').replace(',','.')
|
|
elif ',' in s:
|
|
s = s.replace(',','.')
|
|
try: return float(s)
|
|
except: return None
|
|
```
|
|
|
|
### Tekst
|
|
```python
|
|
def normalize_text(t):
|
|
if not t or pd.isna(t): return ''
|
|
return re.sub(r'\s+',' ',str(t).lower().strip()).strip()
|
|
```
|
|
|
|
---
|
|
|
|
## Konto-specifikt mønster
|
|
|
|
### "Coop 0010014808"
|
|
- Uniconta indeholder mange poster med teksten "Coop 0010014808"
|
|
- Beløb varierer fra -1.155.458,24 til -65.000
|
|
- Nogle har fakturanummer, andre har ikke
|
|
- Dette ligner en **samlet konto-kontering** for Coop-omsætning, ikke enkeltfakturaer
|
|
- Disse poster er svære at matche automatisk og bør behandles særskilt
|
|
|
|
### Bonus/rabat-poster
|
|
- Coop KG: "CCM Purchasing Rebate Credit"
|
|
- Uniconta Rabat: sjælden, men findes
|
|
- Disse bør matches separat via beløb + dato + bonus-tekst
|
|
|
|
---
|
|
|
|
## Specifikke problemer opdaget (V6.1)
|
|
|
|
1. **Faktura 46344** — Uniconta Faktura +441.720, Coop RE -441.720, men også Uniconta Betaling -430.732,50. Delvis kreditnota.
|
|
|
|
2. **Faktura 51519** — Uniconta Faktura +244.926, Coop RE -244.926, Uniconta Betaling -244.926. Dobbeltregistrering (Coop+Uni betaling).
|
|
|
|
3. **Faktura 48323** — Ingen Coop-betaling, men Uniconta Betaling -240.000 findes. Faktura er BETALT via Uniconta-betaling.
|
|
|
|
4. **Faktura 47140/47347 (DELVIST BETALT)** — Coop har både negativ og positiv RE med samme reference. Netto-sum giver korrekt status.
|
|
|
|
5. **Dobbelt-registrering** — Samme betaling findes som både Coop RE og Uniconta Betaling. Ved summing af begge får man 2x betalt. Løsning: brug netto-sum og dedupliker.
|
|
|
|
6. **MR8M-modregninger** — Positive Coop RE med "MR8M Credit memo" tekst er modregninger. De skal trækkes fra i netto-beregning, ikke lægges til med `abs()`.
|
|
|
|
---
|
|
|
|
## Afstemningsstrategi der virkede (V6.1 anbefalet)
|
|
|
|
**Fase 1: Reference-match (RE↔Faktura, RG↔Kreditnota)**
|
|
- Krav: Coop.Reference == Uniconta.Faktura
|
|
- Krav: abs(Coop.Amount) ≈ abs(Uniconta.Amount) ±5 kr (tolerance)
|
|
- Krav: Dato ±21 dage
|
|
- Bonus: Korrekt konteringstype
|
|
- Score ~250
|
|
- Resultat 2024-2025: ~2.421 matches
|
|
|
|
**Fase 2: ZV↔Faktura**
|
|
- Coop ZV (positive fakturaer) matcher Uniconta Faktura
|
|
- abs(beløb) ±5 kr + dato ±21 dage
|
|
- Bonus: Document Number == Faktura
|
|
- Resultat 2024-2025: ~173 matches
|
|
|
|
**Fase 3: RE↔Betaling uden reference**
|
|
- Coop RE uden fakturanummer matcher Uniconta Betaling
|
|
- abs(beløb) ±5 kr + dato ±21 dage
|
|
- Bonus: tekst-lighed
|
|
- Resultat 2024-2025: ~192 matches
|
|
|
|
**Fase 4: Generel abs(beløb)+dato**
|
|
- abs(beløb) ±5 kr + dato ±21 dage
|
|
- Konteringstype-bonus, tekst-lighed, reference-bonus
|
|
- Score ≥ 40
|
|
- Resultat 2024-2025: ~1.939 matches
|
|
|
|
**Fase 5: Mange-til-én**
|
|
- Sum af 2-3 Uniconta-poster == Coop-postering ±5 kr
|
|
- Dato ±14 dage, max 25 kandidater
|
|
- Resultat 2024-2025: ~276 matches
|
|
|
|
**Total matches 2024-2025: 5.001 ud af 7.328 Coop / 6.717 Uniconta (~68%)**
|
|
|
|
**Uafstemt: ~2.327 Coop (31,8%) + ~2.211 Uniconta (32,9%)**
|
|
|
|
---
|
|
|
|
## Faktura-status: Netto-baseret + deduplikering
|
|
|
|
### Dobbelt-registrering
|
|
Samme betaling findes som både Coop RE og Uniconta Betaling.
|
|
|
|
```python
|
|
# Beregn NETTO Coop betalinger (ikke abs-sum!)
|
|
coop_net = sum(c['amount'] for c in coop
|
|
if c['reference'] == fnr and c['doc_type'] in ('RE','ZP'))
|
|
coop_paid = abs(coop_net) if coop_net < 0 else 0
|
|
|
|
# Beregn NETTO Uniconta betalinger
|
|
uni_net = sum(u['amount'] for u in uni
|
|
if u['faktura'] == fnr and u['konteringstype'] == 'Betaling')
|
|
uni_paid = abs(uni_net) if uni_net < 0 else 0
|
|
|
|
# Dedupliker
|
|
if coop_paid > 0 and uni_paid > 0:
|
|
if abs(coop_paid - uni_paid) <= AMT_TOL:
|
|
betalt = coop_paid # Samme betaling
|
|
else:
|
|
betalt = max(coop_paid, uni_paid)
|
|
elif coop_paid > 0: betalt = coop_paid
|
|
elif uni_paid > 0: betalt = uni_paid
|
|
else: betalt = 0
|
|
```
|
|
|
|
**Resultat:** 2.968 BETALT, 2 DELVIST, 186 UBETALT
|
|
|
|
---
|
|
|
|
## Bevar original rådata
|
|
|
|
Exportér ALTID:
|
|
- Original række-nummer (Excel-rækkeindeks)
|
|
- Filnavn og ark-navn
|
|
- Rå JSON med alle kolonner
|
|
Så brugeren kan spore enhver match tilbage til kildefilen.
|