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:
184
AGENTS.md
Normal file
184
AGENTS.md
Normal 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
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user