Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ optima_payment/
optima_payment/doctype/ # this app's doctypes (bank_guarantee_bg, letter_of_credit, cheque_*)
cheque/ # cheque GL + payment entry override logic
purchases_bulk/ # Purchases Bulk Tool: pure decisions + voucher create/rollback (vouchers.py), form door (api.py)
overdue_limit/ # Time Based Overdue Limit: pure decisions + queries + check (limit.py), hooked from doc_events/
override/ # Python class / whitelist overrides
tests/utils.py # SHARED test factories (import these, don't build docs inline)
docs/ # developer + user documentation (start at docs/README.md)
Expand Down Expand Up @@ -61,9 +62,10 @@ mandatory before submit. Issue **commission is never reversed** on return/cancel

- `FrappeTestCase` (unittest), isolated by `frappe.db.rollback()` in `tearDown`. No mocking.
- Most suites are live-DB integration tests. Code that keeps its decisions in plain functions
pairs two files instead: `test_<x>.py` checks every branch with plain rows and writes nothing,
and `test_<x>_integration.py` covers only what a real save shows. Example:
`purchases_bulk/tests/`.
pairs two files instead: `test_<x>.py` checks those decisions with plain rows and writes
nothing, and `test_<x>_integration.py` covers only what a real save shows. A matrix case must
name the plausible bug it catches; one-liners, the code's own constants and message wording get
none. Example: `overdue_limit/tests/`.
- Build fixtures with the factories in `optima_payment/tests/utils.py`
(`make_optima_payment_setting`, `make_bank_guarantee_bg`, `make_letter_of_credit`). They
are defensive and work around this bench's KSA/ZATCA mandatory-field customizations.
Expand Down
5 changes: 3 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Documentation is organized **by domain**. Each folder has its own index.
| [Letter of Credit](letter-of-credit/) | Accountants, implementers & developers | [letter-of-credit/](letter-of-credit/README.md) |
| [Purchases Bulk Tool](purchases-bulk-tool/) | Accountants, implementers & developers | [purchases-bulk-tool/](purchases-bulk-tool/README.md) |
| [Statement of Account](party-statement/) | Accountants, implementers & developers | [party-statement/](party-statement/README.md) |
| [Time Based Overdue Limit](overdue-limit/) | Accountants, implementers & developers | [overdue-limit/](overdue-limit/README.md) |
| [Setup framework](setup/) | Developers | [setup/](setup/README.md) |

## Cross-cutting
Expand All @@ -23,5 +24,5 @@ Documentation is organized **by domain**. Each folder has its own index.

### Quick links by audience

- **Accountants / implementers:** [Cheque](cheque/user-guide.md) · [Bank Guarantee](bank-guarantee/user-guide.md) · [Letter of Credit](letter-of-credit/user-guide.md) · [Purchases Bulk Tool](purchases-bulk-tool/user-guide.md) · [Statement of Account](party-statement/user-guide.md)
- **Developers:** [Cheque](cheque/reference.md) · [Bank Guarantee](bank-guarantee/reference.md) · [Letter of Credit](letter-of-credit/reference.md) · [Purchases Bulk Tool](purchases-bulk-tool/reference.md) · [Statement of Account](party-statement/reference.md) · [Setup architecture](setup/architecture.md) · [Setup Q&A](setup/questions-and-answers.md) · [Testing](development/testing.md)
- **Accountants / implementers:** [Cheque](cheque/user-guide.md) · [Bank Guarantee](bank-guarantee/user-guide.md) · [Letter of Credit](letter-of-credit/user-guide.md) · [Purchases Bulk Tool](purchases-bulk-tool/user-guide.md) · [Statement of Account](party-statement/user-guide.md) · [Overdue Limit](overdue-limit/user-guide.md)
- **Developers:** [Cheque](cheque/reference.md) · [Bank Guarantee](bank-guarantee/reference.md) · [Letter of Credit](letter-of-credit/reference.md) · [Purchases Bulk Tool](purchases-bulk-tool/reference.md) · [Statement of Account](party-statement/reference.md) · [Overdue Limit](overdue-limit/reference.md) · [Setup architecture](setup/architecture.md) · [Setup Q&A](setup/questions-and-answers.md) · [Testing](development/testing.md)
19 changes: 18 additions & 1 deletion docs/development/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,19 @@ creates survives the test.
assert on those Payment Entries (`get_lc_payment_entries`, filtered by the `is_lc_*`
flags). See [letter-of-credit/reference.md](../letter-of-credit/reference.md).
- **Purchases Bulk Tool** keeps its decisions in plain functions, so its tests come as a pair.
`test_vouchers.py` checks every branch with plain rows and writes nothing; it runs in
`test_vouchers.py` checks those decisions with plain rows and writes nothing; it runs in
milliseconds and needs no data on the site. `test_vouchers_integration.py` asserts on the real
vouchers (lines, header dates, docstatus) and the row status / reference stamps. See
[purchases-bulk-tool/reference.md](../purchases-bulk-tool/reference.md).
- **Time Based Overdue Limit** pairs the same way. `test_limit.py` runs the rule precedence and how
triggered rules combine on plain rules; `test_limit_integration.py` saves and submits real Sales
Invoices and Sales Orders for a customer with an invoice 25 days overdue. See
[overdue-limit/reference.md](../overdue-limit/reference.md).

A matrix case earns its place by naming the plausible bug it catches: how rules combine, a
boundary, a reachable edge case, or a spec table from the docs. A one-liner, a loop over the code's
own constant, and a message's wording get no case; assert which line a branch produces, not the
sentence. `overdue_limit/tests/test_limit.py` is the worked example.

---

Expand All @@ -40,6 +49,12 @@ factories instead of building documents inline:
| `get_or_create_purchase_item(company, item_code, is_stock_item=…)` | A purchase Item taxed by the suite's own Item Tax Template, its tax row carrying the test suppliers' tax category so that template applies; pass `is_stock_item=1` for Purchase Receipt tests |
| `get_or_create_purchase_item_tax_template(company)` | The Item Tax Template behind those items, on the same account and rate |
| `set_test_cost_center_default(company)` | Makes a leaf cost center the session user's `cost_center` default, so the tool's vouchers carry one on sites that make it mandatory on Purchase Invoice. It lives in the defaults cache: pair it with `frappe.clear_cache(user=frappe.session.user)` after the rollback |
| `make_sales_order(customer=…, do_not_insert=…, do_not_submit=…, **overrides)` | A one-line Sales Order on the test item, cost center and pre-seeded tax row; `make_reference_sales_order` wraps it |
| `make_sales_invoice(customer=…, posting_date=…, due_date=…, do_not_insert=…, do_not_submit=…, **overrides)` | A one-line Sales Invoice; a past `posting_date` is kept, and the due date defaults to it. The header has no cost center, as on real invoices, because a payment copies it onto balance sheet lines that some sites refuse one on |
| `make_invoice_overdue(invoice, days)` | Moves the invoice's due date and payment schedule `days` into the past, then runs ERPNext's `update_invoice_status` so the invoice turns Overdue as it would on a live site |
| `make_credit_note(invoice)` | An unsaved full return of the invoice, with the return reason some sites require |
| `make_invoice_payment(invoice, allocate=…)` | A submitted Payment Entry for the invoice's outstanding amount; `allocate=False` leaves it unallocated, so the invoice stays outstanding while the customer owes nothing |
| `make_overdue_limit(party=…, action=…, threshold_days=…, applies_to=…, company=…, exempt_roles=…, enabled=…)` | A Time Based Overdue Limit rule |

The factories are defensive (`get_or_create_*`) and work around this bench's KSA/ZATCA
customizations (mandatory Arabic name fields, tax category, item tax templates, order-level
Expand All @@ -57,6 +72,8 @@ bench --site <site> run-tests --module optima_payment.optima_payment.doctype.let
bench --site <site> run-tests --module optima_payment.optima_payment.doctype.bank_guarantee_bg.test_bank_guarantee_bg
bench --site <site> run-tests --module optima_payment.purchases_bulk.tests.test_vouchers
bench --site <site> run-tests --module optima_payment.purchases_bulk.tests.test_vouchers_integration
bench --site <site> run-tests --module optima_payment.overdue_limit.tests.test_limit
bench --site <site> run-tests --module optima_payment.overdue_limit.tests.test_limit_integration
```

The site needs `optima_payment` installed and `allow_tests` enabled
Expand Down
16 changes: 16 additions & 0 deletions docs/overdue-limit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Time Based Overdue Limit — Docs

Everything about warning about or blocking sales to a customer whose invoices have been unpaid too
long past their due date.

| Document | For | What it covers |
|----------|-----|----------------|
| [user-guide.md](user-guide.md) | Accountants & implementers | Setting up a rule, the three actions, which rule wins, how the days are counted, exempt roles, what is never checked, troubleshooting |
| [reference.md](reference.md) | Developers | Which hook checks each action, rule selection by tier, the day-count query, the outcome table, the blank company, known limits, tests |

The feature is layered on **Time Based Overdue Limit**, with the child table **Time Based Overdue
Limit Exempt Role**. It checks standard **Sales Invoice** and **Sales Order** documents when they
are saved and submitted. It is ERPNext's Credit Limit keyed on how long the debt has been overdue
instead of how large it is.

See also: [Testing](../development/testing.md) for how the suite is written and run.
Loading
Loading