185 lines
6.7 KiB
Markdown
185 lines
6.7 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```ruby
|
|
# 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
|
|
|
|
```ruby
|
|
# 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 |
|
|
|
|
## Related Files
|
|
|
|
- `docs/reconciliation_plan.md` — Original design document
|
|
- `docs/dolibarr.json` — Dolibarr API spec
|
|
- `.env.example` — Environment variable template
|