Initial import: 10 Radix skills across 3 categories

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.
This commit is contained in:
2026-06-15 14:41:04 +02:00
commit 236053cd7f
30 changed files with 4460 additions and 0 deletions
@@ -0,0 +1,235 @@
# 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.