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.
|
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
|
## The problem it solves
|
||||||
|
|
||||||
Payments flow through three separate systems that are not automatically linked:
|
Payments flow through three separate systems that are not automatically linked:
|
||||||
|
|||||||
Reference in New Issue
Block a user