Skip to content
Back to skills

Document Parsing

ASecurity

Extraction rules for receipts and invoices. Reference this when processing uploaded receipts or PDF invoices.

  • 5 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 26, 2026
developmentgoapi

Works with

  • terminal
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add peerjakobsen/smartspender --skill document-parsing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Document Parsing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Document Parsing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/peerjakobsen-document-parsing/badge)](https://www.skillsdirectory.com/skills/peerjakobsen-document-parsing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: document-parsing
description: Extraction rules for receipts and invoices. Reference this when processing uploaded receipts or PDF invoices.
---

# Document Parsing

## 1. Overview

Provides Claude with extraction rules for converting receipt images and PDF invoices into structured line-item data. This skill covers:
- Danish grocery receipts (thermal paper with many line items)
- Telecom invoices (PDF with service line items)
- Utility bills (PDF with consumption data)
- Restaurant receipts
- Online order confirmations

It also provides a workflow for looking up and applying vendor-specific invoice parsers from `invoice-knowledge/`.

---

## 2. Vendor Detection & Parser Lookup

### Parser Lookup Workflow

When processing a receipt or invoice, follow this decision tree:

```
1. Detect vendor (see Vendor Detection below)
2. Normalize vendor name to vendor-id (lowercase, no spaces)
3. Check: does invoice-knowledge/{vendor-id}/PARSER.md exist?
   ├── YES: Load PARSER.md → use vendor-specific extraction rules
   │         Set confidence boost: +0.1 (parser-assisted extraction)
   └── NO:  Fall back to general rules (Section 3 and 4 below)
            Set confidence: 0.5-0.7 (general extraction)
            After processing: suggest /smartspender:receipt learn
```

### Vendor Detection Signals

Detect the vendor using these signals in priority order:

| Priority | Signal | Source | Example |
|----------|--------|--------|---------|
| 1 | Email sender domain | Email metadata (receipt email workflow) | `faktura@tdc.dk` → TDC |
| 2 | Filename | Uploaded file name | `faktura_orsted_jan2026.pdf` → Oersted |
| 3 | Invoice header/logo | First page content (Claude Vision) | Logo text "HOFOR" → HOFOR |
| 4 | Content keywords | Invoice body text | "TDC NET A/S" → TDC |
| 5 | Transaction match | Bank transaction description | "PBS TDC" → TDC |

### Vendor ID Normalization

| Vendor Name | Vendor ID | Aliases |
|-------------|-----------|---------|
| TDC | `tdc` | TDC NET, TDC A/S, TDC Erhverv |
| HOFOR | `hofor` | HOFOR A/S |
| Oersted | `orsted` | Oersted Salg, Oersted A/S, Oersted Danmark |
| Norlys | `norlys` | Norlys Energi, SE (former name) |
| Telenor | `telenor` | Telenor Danmark |
| Telia | `telia` | Telia Danmark, Telia Company |

For vendors not in this table: normalize to lowercase, replace spaces with hyphens, remove A/S and other suffixes.

### Parser Application

When a PARSER.md is found, apply it as follows:

1. **Load Parser**: Read `invoice-knowledge/{vendor-id}/PARSER.md` into context
2. **Extract Metadata**: Use the parser's extraction rules instead of generic rules
3. **Extract Line Items**: Use the parser's line items table to map invoice lines to categories
4. **Apply Special Handling**: Follow any special handling rules in the parser
5. **Check Learned Corrections**: Apply corrections from the Learned Corrections table
6. **Set Confidence**: Parser-assisted extractions get baseline 0.8 (vs. 0.5-0.7 for general)

### Fallback Behavior

When no vendor-specific parser exists:

1. Use general extraction rules from sections 3-4 below
2. Set extraction confidence to 0.5-0.7 (lower than parser-assisted)
3. After processing completes and the user has verified/corrected the extraction, suggest:

```
Tip: Du kan koere /smartspender:receipt learn for at gemme
udtraeksregler for {vendor}, saa fremtidige fakturaer
fra denne leverandoer bliver mere praecise.
```

Only suggest this for PDF invoices (not grocery receipts or simple card slips).

### Available Parsers

| Vendor | Type | Parser Path |
|--------|------|-------------|
| TDC | Telecom | `invoice-knowledge/tdc/PARSER.md` |
| HOFOR | Water utility | `invoice-knowledge/hofor/PARSER.md` |
| Oersted | Electricity | `invoice-knowledge/orsted/PARSER.md` |

---

## 3. Grocery Receipt Extraction

### Supported Receipt Types

| Type | Common Sources | Key Characteristics |
|------|----------------|---------------------|
| Grocery receipt | Foetex, Netto, Rema 1000, Bilka, Meny | Thermal paper, many line items, Danish keywords |
| Restaurant receipt | Restaurants, cafes | Total + tip, few items, sometimes handwritten |
| Online order | Amazon, Zalando, IKEA | PDF/email, shipping line, order number |

### Structure Recognition

Danish grocery receipts follow a consistent layout:

```
[Store name and address]
[Date and time]
[Cashier / register info]
---
[Item lines]
...
---
[Subtotal]
[Discounts]
[Total]
[Payment method]
[VAT summary]
```

### Item Line Patterns

Each item line typically follows one of these formats:

| Pattern | Example | Notes |
|---------|---------|-------|
| `Name  Price` | `Minimælk 1L  12.95` | Single item |
| `Qty x Name  Price` | `2 x Minimælk 1L  25.90` | Multiple quantity |
| `Qty STK Name  Price` | `2 STK Minimælk 1L  25.90` | Alternative quantity format |
| `Name  Price A/B` | `Minimælk 1L  12.95 A` | With VAT code suffix |

### Danish Keywords

| Keyword | Meaning | Action |
|---------|---------|--------|
| `I ALT` | Total | Extract as receipt total |
| `TOTAL` | Total | Extract as receipt total |
| `SUM` | Sum | Extract as receipt total |
| `RABAT` | Discount | Extract as line discount |
| `TILBUD` | Offer/sale | Line has a promotional price |
| `PANT` | Bottle deposit | Extract as separate line item (category: Andet) |
| `POSE` | Bag fee | Extract as separate line item (category: Andet) |
| `STK` | Pieces (quantity) | Parse as quantity indicator |
| `KG` | Kilograms | Parse as weight-based quantity |
| `KONTANT` | Cash | Payment method — skip |
| `DANKORT` | Debit card | Payment method — skip |
| `VISA` | Credit card | Payment method — skip |
| `MOBILEPAY` | Mobile payment | Payment method — skip |
| `MOMS` | VAT | VAT summary — skip |
| `SUBTOTAL` | Subtotal | Before discounts — skip (use I ALT) |
| `RETUR` | Return | Negative line item |
| `BYTTEPENGE` | Change | Cash change — skip |

### Discount Handling

Discounts on Danish receipts appear in several formats:

| Format | Example | Rule |
|--------|---------|------|
| Inline discount | `RABAT -5.00` on next line after item | Apply discount to preceding item |
| Percentage | `10% RABAT` | Calculate discount from item price |
| Member discount | `COOP RABAT -3.00` | Apply to preceding item, note as member discount |
| Multi-buy | `3 FOR 2 RABAT -12.95` | Apply discount to the item group |

### Total Validation

After extracting all items, compare the sum of line totals against the receipt total:
- **Variance <= 2%**: Accept without comment
- **Variance 2-5%**: Accept but note: "Bemaerk: Summen af varerne afviger lidt fra totalen (pant, afrunding, eller manglende linje)."
- **Variance > 5%**: Flag for review: "Summen af varerne ({sum} kr) matcher ikke kvitteringens total ({total} kr). Tjek venligst om alle linjer er korrekt aflaest."

---

## 4. PDF Invoice Extraction

### Structure Recognition

Danish PDF invoices typically contain:

```
[Company logo and name]
[Invoice number and date]
[Customer info]
[Billing period]
---
[Service line items]
---
[Subtotal before VAT]
[Moms (25%)]
[Total including moms]
[Payment info / due date]
```

### Key Fields to Extract

| Field | Where to Find | Example |
|-------|---------------|---------|
| Merchant | Header/logo area | TDC, Oersted, HOFOR |
| Invoice date | Near invoice number | Fakturadato: 15-01-2026 |
| Billing period | Below customer info | Periode: 01-12-2025 til 31-12-2025 |
| Line items | Main table | Mobilabonnement Frihed+ — 199,00 |
| Moms | Before total | Moms 25% — 59,75 |
| Total | Bottom of invoice | I alt inkl. moms — 299,00 |

### Danish Number Formats in PDFs

Danish invoices use comma as decimal separator and period as thousands separator:
- `1.234,56` means 1234.56
- `299,00` means 299.00

Convert all amounts to standard decimal format (period as decimal separator) for storage.

---

## 5. Product Subcategory Taxonomy

Product-level subcategories for receipt line items. These are more granular than the merchant-level subcategories in `skills/categorization/SKILL.md`.

### Dagligvarer (Groceries) Subcategories

| Subcategory | Examples | Keywords |
|-------------|----------|----------|
| Mejeriprodukter | Maelk, yoghurt, ost, smoer | maelk, yoghurt, ost, smoer, floede, skyr |
| Koed | Hakket oksekoed, kylling, poelse | koed, kylling, svin, okse, poelse, bacon, lam |
| Fisk | Laks, torsk, rejer | laks, torsk, rejer, fisk, tun, sild |
| Frugt og groent | Bananer, aebler, tomater, salat | frugt, groent, banan, aeble, tomat, salat, loeg, kartoffel, gulerod |
| Broed og bagvaerk | Rugbroed, franskbroed, boller | broed, rug, bolle, kage, croissant, bagvaerk |
| Drikkevarer | Sodavand, juice, vand | cola, pepsi, fanta, juice, vand, sodavand, oel (non-alcoholic) |
| Alkohol | Vin, oel, spiritus | vin, oel, spiritus, vodka, gin, whisky, champagne |
| Frostvarer | Frosne pizzaer, is, frosne groentsager | frost, frossen, is, pizza (frozen) |
| Konserves | Daaser, pasta, ris, mel | konserves, daase, pasta, ris, mel, sukker, salt |
| Snacks | Chips, chokolade, slik | chips, chokolade, slik, noedder, popcorn, kiks |
| Personlig pleje | Shampoo, tandpasta, deodorant | shampoo, tandpasta, deodorant, saebe, creme |
| Rengoring | Opvaskemiddel, vaskepulver | rengoring, opvask, vaske, aftorring, toiletpapir |
| Husdyr | Hundefoder, kattesand | hund, kat, foder, husdyr |

### Bolig (Housing) Subcategories

| Subcategory | Examples | Keywords |
|-------------|----------|----------|
| El | Elforbrug, elafregning | el, kwh, forbrug, afregning |
| Vand | Vandforbrug | vand, m3, forbrug |
| Varme | Fjernvarme, gasforbrug | varme, fjernvarme, gas |
| Internet | Bredbaand | internet, bredbaand, fiber, wifi |

### Abonnementer (Subscriptions) Subcategories

| Subcategory | Examples | Keywords |
|-------------|----------|----------|
| Mobilabonnement | Mobiltjeneste, datatilaeg | mobil, data, tale, sms, opkald |
| Forsikring | Indboforsikring, mobilforsikring | forsikring, daekning |
| Streaming | Netflix, Spotify | (matched by merchant, not keywords) |
| Software | Adobe, Microsoft 365 | (matched by merchant, not keywords) |

---

## 6. Confidence Scoring

Rate the quality of each extraction:

| Confidence | Criteria |
|------------|----------|
| 0.9-1.0 | Clear image/PDF, all fields extracted, total validates |
| 0.7-0.8 | Mostly readable, 1-2 items uncertain, total within 5% |
| 0.5-0.6 | Partially readable, multiple items uncertain or missing |
| 0.3-0.4 | Poor quality, only merchant/date/total extractable |
| 0.0-0.2 | Unreadable or not a receipt |

Store the confidence in `match_confidence` on the receipts.csv row (this is extraction confidence, separate from the transaction match confidence in `skills/transaction-matching/SKILL.md`).

---

## 7. Edge Cases

### Blurry or Low-Quality Receipts
If the receipt image is too blurry to extract reliably:
1. Extract whatever is readable (merchant, date, total at minimum)
2. Set confidence to 0.3-0.4
3. Set item_count to 0 if no items are readable
4. Inform user: "Kvitteringen er svaer at aflaese. Jeg kunne kun finde butik, dato og total. Vil du tilfoeje varelinjer manuelt?"

### Multiple Receipts in One Image
If the image contains more than one receipt:
1. Process only the first/most prominent receipt
2. Inform user: "Billedet ser ud til at indeholde flere kvitteringer. Jeg har behandlet den foerste. Del venligst de andre enkeltvis."

### Non-Danish Receipts
If the receipt is not in Danish:
1. Still attempt extraction (most receipt formats are similar)
2. Use the receipt's currency if not DKK
3. Note in file_reference: "non-danish"
4. Categories still use Danish names

### Receipts Without Line Items
Some receipts only show a total (e.g., card terminal slips):
1. Create the receipts.csv row with item_count = 0
2. Skip writing to receipt-items files (don't create an empty monthly file)
3. Inform user: "Denne kvittering har kun en total — ingen varelinjer fundet."

### Negative Items (Returns)
Items marked with `RETUR` or negative amounts:
1. Store with negative total_price
2. Category matches the original item if identifiable
3. Discount field stays 0 (the negative price is the return, not a discount)

---

## 8. Examples

### Example 1: Grocery Receipt Extraction

**Input**: Photo of Foetex receipt

**Extracted metadata**:
```
merchant: Foetex
date: 2026-01-28
total: 347.50
currency: DKK
source: upload
```

**Extracted items**:
```
1. Minimælk 1L          qty:2  unit:12.95  total:25.90  cat:Dagligvarer  sub:Mejeriprodukter
2. Hakket oksekød 500g  qty:1  unit:45.00  total:45.00  cat:Dagligvarer  sub:Kød
3. Øko bananer          qty:1  unit:22.95  total:22.95  cat:Dagligvarer  sub:Frugt og grønt
4. Coca-Cola 1.5L       qty:2  unit:22.00  total:44.00  cat:Dagligvarer  sub:Drikkevarer
5. Rødvin Chianti       qty:1  unit:79.95  total:79.95  cat:Dagligvarer  sub:Alkohol
6. Tandbørstehoveder    qty:1  unit:89.95  total:89.95  cat:Sundhed      sub:Personlig pleje
7. Rugbrød              qty:1  unit:24.95  total:24.95  cat:Dagligvarer  sub:Brød og bagværk
8. Pose 2 kr            qty:1  unit:2.00   total:2.00   cat:Andet        sub:Andet
```

Sum: 334.70 vs total 347.50 — variance 3.7% — accept with note about pant/rounding.

### Example 2: TDC Invoice Extraction

**Input**: PDF invoice from TDC

**Extracted metadata**:
```
merchant: TDC
date: 2026-01-15
total: 299.00
currency: DKK
source: upload
```

**Extracted items**:
```
1. Mobilabonnement Frihed+  qty:1  unit:199.00  total:199.00  cat:Abonnementer  sub:Mobilabonnement
2. Ekstra data 10GB         qty:1  unit:49.00   total:49.00   cat:Abonnementer  sub:Mobilabonnement
3. Forsikring Mobil+        qty:1  unit:51.00   total:51.00   cat:Abonnementer  sub:Forsikring
```

Sum: 299.00 vs total 299.00 — exact match.

### Example 3: Known Vendor (TDC) with Parser

**Input**: PDF invoice uploaded, filename "TDC_faktura_jan2026.pdf"

**Vendor detection**:
1. Filename contains "TDC" → vendor candidate: TDC
2. Check `invoice-knowledge/tdc/PARSER.md` → exists
3. Load TDC parser

**Parser-assisted extraction**:
- date: Look for "Fakturadato" → 15-01-2026
- total: Look for "I alt inkl. moms" → 299,00
- Line items: Match "Mobilabonnement" → Abonnementer > Mobilabonnement

**Confidence**: 0.95 (all fields extracted cleanly with parser)

### Example 4: Unknown Vendor (Norlys, No Parser Yet)

**Input**: PDF invoice from Norlys

**Vendor detection**:
1. Invoice header shows "Norlys" → vendor candidate: Norlys
2. Normalize to vendor-id: `norlys`
3. Check `invoice-knowledge/norlys/PARSER.md` → does not exist

**Fallback extraction**:
- Use general PDF invoice rules
- Extract merchant, date, total, line items using generic patterns

**Confidence**: 0.6 (general extraction, no vendor-specific rules)

**Post-processing**: After user verifies, suggest:
"Tip: Du kan koere /smartspender:receipt learn for at gemme udtraeksregler for Norlys..."

---

## Related Skills

- See `skills/data-schemas/SKILL.md` for the CSV file structure
- See `skills/transaction-matching/SKILL.md` for how extracted receipts are matched to bank transactions
- See `skills/categorization/SKILL.md` for merchant-level categories (receipt subcategories are more granular)
- See `skills/email-receipt-scanning/SKILL.md` for email-specific vendor detection signals

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.