Files
dolibarr_shine_reconciliation/AGENTS.md
2026-02-26 00:45:49 +01:00

6.7 KiB

Agent Instructions — Dolibarr / GoCardless / Shine Reconciliation

Project Goal

Reconcile invoices and payments across three financial systems:

  • Dolibarr — Invoice management (via REST API)
  • GoCardless — Direct debit payments (via CSV export)
  • Shine — Bank account (via CSV export)

Design Principles

  1. Manual operation — CSV-based, no background processes, no API integrations with payment providers
  2. Standalone — No coupling to console.cyanet.fr or other projects
  3. Read-only by default--fix mode is opt-in and only affects invoices flagged GC_PAID_DOLIBARR_OPEN
  4. Audit trail — CSV exports serve as snapshots; output CSVs written to tmp/

Architecture

bin/reconcile                          # CLI entry point
lib/
  boot.rb                              # Dependency loader
  dolibarr/client.rb                   # HTTP client (HTTParty)
  reconciliation/
    dolibarr_fetcher.rb                # Fetch invoices + customer names
    gocardless_parser.rb               # Parse payments CSV
    gocardless_payouts_parser.rb       # Parse payouts CSV (fees)
    shine_parser.rb                    # Parse Shine bank CSV
    engine.rb                          # 3-pass matching logic
    reporter.rb                        # Terminal report + CSV export
    fixer.rb                           # Apply --fix mode

Data Flow

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  GoCardless │     │   Dolibarr   │     │    Shine    │
│   payments  │     │   invoices   │     │  transactions│
│     CSV     │     │     API      │     │     CSV     │
└──────┬──────┘     └──────┬───────┘     └──────┬──────┘
       │                   │                    │
       ▼                   ▼                    ▼
┌─────────────────────────────────────────────────────────┐
│                      Engine                             │
│  Pass 1: GC payments ↔ Dolibarr invoices                │
│  Pass 2: Open Dolibarr invoice audit                    │
│  Pass 3: GC payouts ↔ Shine bank credits                │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │  Reporter + Fixer      │
              │  Terminal report       │
              │  CSV export to tmp/    │
              │  Optional --fix        │
              └────────────────────────┘

Matching Logic

Pass 1 — GC ↔ Dolibarr

Match Type Criteria
Strong payment.description == invoice.ref (case-insensitive)
Soft Same amount + customer name + date within ±7 days

Pass 2 — Open Invoice Audit

All open Dolibarr invoices with no GC match → DOLIBARR_OPEN_NO_GC

Pass 3 — GC Payouts ↔ Shine

Match Type Criteria
Strong Payout reference found in Shine Libellé
Fallback Exact net amount + date within ±2 days

Key Files to Modify

Task File
Add new flag lib/reconciliation/engine.rb (constants + Match struct)
Change matching logic lib/reconciliation/engine.rb (pass1/2/3 methods)
Add parser field lib/reconciliation/*_parser.rb (Payment/Transaction/Invoice struct)
Change report output lib/reconciliation/reporter.rb (print_summary, write_csv)
Add CLI option bin/reconcile (OptionParser + wiring)
Dolibarr API calls lib/dolibarr/client.rb, lib/reconciliation/dolibarr_fetcher.rb

Coding Conventions

  • Ruby 3.x — Use frozen_string_literal: true
  • No external dependencies — Only stdlib + HTTParty (already in Gemfile)
  • Amounts in cents — Always use integers to avoid float precision issues
  • Date tolerance — Use DATE_TOLERANCE / PAYOUT_DATE_TOLERANCE constants (configurable via ENV)
  • Name normalization — Strip accents, lowercase, sort words for comparison

Testing Checklist

Before committing changes:

# Syntax check
ruby -c lib/reconciliation/*.rb
ruby -c bin/reconcile

# Run reconciliation (dry run)
ruby bin/reconcile --from 2026-01-01 --to 2026-01-31 \
  --gc gocardless/*.csv --gc-payouts gocardless/*.csv \
  --shine shine/*/*.csv

# Verify CSV output
cat tmp/reconciliation_*.csv
cat tmp/payouts_fees_*.csv

Common Patterns

Adding a new flag

# 1. Define constant in Engine
NEW_FLAG = :new_flag

# 2. Add to Match struct
Match = Struct.new(
  :flag, :invoice, :payment, :match_type, :partial, :retry_group,
  :new_field,  # if needed
  keyword_init: true
)

# 3. Set flag in matching logic
results << Match.new(flag: NEW_FLAG, ...)

# 4. Handle in reporter
r[:new_flag_matches].each do |m|
  puts "  #{m.invoice.ref}"
end

Adding a new parser field

# 1. Add to struct
Payment = Struct.new(
  :id, :charge_date, :amount_cents, :new_field,
  keyword_init: true
)

# 2. Parse from CSV
Payment.new(
  id: row["id"].to_s.strip,
  new_field: row["new_column"].to_s.strip,
  ...
)

# 3. Use in engine/reporter

Gotchas

  1. Dolibarr status=1 — Returns all non-draft invoices (open + paid + cancelled). Filter by statut field in response.
  2. Shine CSV format — Semicolon separator, French decimal (51,10), Windows CRLF
  3. GoCardless fees — Payout net amount < sum of payments (fee deducted). This is normal.
  4. Credit notes — Negative total_ttc invoices are excluded from reconciliation
  5. Supplier invoices — Separate API endpoint (/supplierinvoices), not handled

Environment Variables

Variable Default Purpose
DOLIBARR_URL Dolibarr API base URL
DOLIBARR_API_KEY API authentication
DOLIBARR_GC_PAYMENT_ID Payment method ID for --fix
DOLIBARR_BANK_ACCOUNT_ID 1 Bank account ID for --fix
RECONCILIATION_DATE_TOLERANCE 7 Days for soft date matching
RECONCILIATION_PAYOUT_TOLERANCE 2 Days for payout date matching
  • docs/reconciliation_plan.md — Original design document
  • docs/dolibarr.json — Dolibarr API spec
  • .env.example — Environment variable template