6.7 KiB
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
- Manual operation — CSV-based, no background processes, no API integrations with payment providers
- Standalone — No coupling to
console.cyanet.fror other projects - Read-only by default —
--fixmode is opt-in and only affects invoices flaggedGC_PAID_DOLIBARR_OPEN - 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_TOLERANCEconstants (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
- Dolibarr
status=1— Returns all non-draft invoices (open + paid + cancelled). Filter bystatutfield in response. - Shine CSV format — Semicolon separator, French decimal (
51,10), Windows CRLF - GoCardless fees — Payout net amount < sum of payments (fee deducted). This is normal.
- Credit notes — Negative
total_ttcinvoices are excluded from reconciliation - 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 |
Related Files
docs/reconciliation_plan.md— Original design documentdocs/dolibarr.json— Dolibarr API spec.env.example— Environment variable template