Add AGENTS.md for AI coding agents; clarify manual tool design in README

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Kevin Bataille
2026-02-26 00:45:49 +01:00
parent 0a048628a5
commit f31265c064
2 changed files with 186 additions and 0 deletions

184
AGENTS.md Normal file
View File

@@ -0,0 +1,184 @@
# 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

View File

@@ -2,6 +2,8 @@
A standalone Ruby script that cross-checks three financial systems and flags discrepancies.
**Manual tool** — Export CSVs from GoCardless and Shine, run the script locally, review the report. No API integrations with payment providers, no background processes, no coupling to other projects.
## The problem it solves
Payments flow through three separate systems that are not automatically linked: