Test Report — 18.0.1.0.0
What is tested in Customer Payment Reminders, and how to run the suite.
Result: 71 automated tests, 0 failures, 0 errors. Exit code 0.
Every figure here comes from an actual run. The suite is built on Odoo's own accounting fixture, so the invoices it chases are real posted invoices with real journals, taxes and receivable lines — not stubs with a payment_state field set by hand.
Environment
| Odoo | 18.0 (community, source checkout) |
| Python | 3.12.3 |
| PostgreSQL | 16.15 |
| OS | Ubuntu 24.04.1 LTS |
| Database | created fresh for the run |
| Command | odoo-bin -d <db> -i technovision_payment_reminders --test-enable --test-tags /technovision_payment_reminders --stop-after-init |
Result
Tests started : 71
Failures : 0
Errors : 0
Odoo exit code: 0The run emits one ERROR line from PostgreSQL — a negative day count — produced deliberately by test_negative_days_refused.
Coverage by area
| Suite | Tests | Covers |
|---|---|---|
| TestSending | 17 | Sending and logging, the email and its recipient, activities, escalation, the cron, and log pruning |
| TestPartnerState | 17 | What counts as overdue, days overdue, the minimum amount, the displayed level, and the reason shown when nothing is due |
| TestPausing | 15 | Pauses and their expiry, permanent exclusions, forcing a level, and the manual send wizard |
| TestLevels | 12 | The shipped levels, ordering, thresholds and both sides of each boundary, and every refusal |
| TestSecurity | 10 | Controller versus manager versus outsider, on levels and on history; company scoping |
| Total | 71 |
What the tests actually prove
Nothing is sent to the wrong person. test_the_email_reaches_the_invoicing_contact asserts the reminder goes to the partner's invoicing contact rather than to whichever address happens to be on the record, and test_a_customer_without_an_email_is_skipped_not_crashed asserts a missing address is skipped quietly instead of stopping the run.
Nothing is sent twice. test_the_same_level_is_not_sent_twice asserts a level already sent is not sent again, and test_the_escalation_moves_on asserts the next one takes over. test_a_new_debt_restarts_the_escalation asserts a customer who has cleared their balance and fallen behind again starts at the first level rather than resuming at the last one they received.
A customer who does not owe anything is never chased. Separate tests assert that a draft invoice, a paid invoice, an invoice not yet due, and a balance below the configured minimum all produce no reminder — and test_no_overdue_gives_a_reason asserts the interface says why rather than appearing to do nothing.
A pause is honoured, and it ends. test_a_paused_customer_is_not_chased, test_a_pause_ending_today_still_holds, test_an_expired_pause_no_longer_holds and test_expired_pauses_are_cleared cover both edges of the pause window and the job that tidies up afterwards. test_an_excluded_customer_is_never_chased and test_a_manual_send_respects_an_exclusion assert that a permanent exclusion holds even when somebody presses the button by hand.
Two levels cannot fight over the same day. test_two_levels_at_the_same_day_are_refused asserts a level cannot be created at a day count another visible level already claims. The SQL unique constraint cannot manage this alone — the shipped levels have no company, and PostgreSQL counts each NULL company as distinct — so a Python constraint enforces it. The test was written before the constraint and failed against the original code.
The most severe applicable level wins. test_the_most_severe_applicable_level_wins asserts that a customer left alone for months receives the sixty-day letter rather than restarting at seven, and test_the_boundary_day_applies asserts the threshold day itself counts.
The role that sends reminders can send reminders. test_a_controller_can_send_and_log drives a real send as a Credit Controller. It failed against the original code, which recorded the send by writing to the partner directly and so demanded contact-editing rights the role does not have and should not need; the bookkeeping write is now made with elevated rights while everything the user is actually doing stays under their own. test_a_controller_cannot_change_a_level and test_a_controller_cannot_delete_history assert the limits still hold.
One failure does not stop the run. test_one_failure_does_not_stop_the_run asserts the cron completes the remaining customers after one raises, and test_the_cron_does_nothing_when_disabled asserts the module chases nobody until an administrator switches it on.
Known gaps in coverage
Stated rather than glossed over:
- No browser (tour) tests. The settings block, the wizard and the customer fields are covered on the server side; there is no automated click-through of the rendered pages. They were verified manually.
- Mail delivery is not exercised. The suite asserts the message is created, addressed and rendered with the right invoices. Whether your SMTP server delivers it is your mail server's business.
- Multi-currency. The log records the company's currency. A customer invoiced in several currencies is summed in the company currency; that behaviour is asserted but not exercised across a wide set of rates.
- Odoo 19. Not tested; this release targets 18.0 only.
Reproducing
createdb -O odoo apr_test
odoo-bin -c odoo.conf -d apr_test \
-i technovision_payment_reminders --test-enable \
--test-tags /technovision_payment_reminders --stop-after-init
echo "exit code: $?"A clean run prints no FAIL: or ERROR: Test lines and exits 0.