Skip to content

fix(octopus): prefer the direct debit rate when both payment methods overlap - #5145

Open
Navesz wants to merge 1 commit into
springfall2008:mainfrom
Navesz:fix-octopus-payment-method
Open

Navesz wants to merge 1 commit into
springfall2008:mainfrom
Navesz:fix-octopus-payment-method

Conversation

@Navesz

@Navesz Navesz commented Sep 18, 2026

Copy link
Copy Markdown

Closes #5144

The bug

The REST tariff endpoints return two overlapping rows per validity window, one DIRECT_DEBIT and one NON_DIRECT_DEBIT. Both go straight into minute_data(), which writes each row over its range, so whichever row comes last in the response wins.

Reproduced against the live public API (E-1R-VAR-22-11-01-A, no auth), using the repository's own minute_data:

valor que o predbat usa agora : 27.8488
   API DIRECT_DEBIT      = 26.381355
   API NON_DIRECT_DEBIT  = 27.848835
janelas: 17 · com DIRECT_DEBIT por último: 5

So a direct debit customer is shown the higher rate — and the last line is the part that is easy to miss: 5 of the 17 windows the API returns put DIRECT_DEBIT last, so the displayed rate silently flips variant from one period to the next rather than always being the higher one.

payment_method appeared nowhere in the production code before this change — only in test fixtures.

The fix

A module-level filter_payment_method() in octopus.py, applied at both parse points, as suggested in the triage on the issue:

  • get_octopus_rates_direct() — the OctopusAPI component path, for unit rates and standing charges;
  • download_octopus_rates_func() — the legacy import_octopus_url / compare path.

It is deliberately conservative:

  • rows with payment_method: None (Agile, day/night) pass through untouched;
  • rows without the key pass through untouched;
  • a response that never mentions DIRECT_DEBIT is returned unchanged, so a non-direct-debit-only feed keeps its rate rather than becoming empty.

Filtering at the parse point rather than at download also covers data already sitting in the URL and storage caches.

Tests

A seventh sub-test in tests/test_octopus_url.py (payment_method), covering the five cases:

Case Asserts
unit rates, non-direct-debit row last 26.3814 (the order the live API returns today)
unit rates, direct debit row last 26.3814 (the order returned for older periods)
standing charges, same overlap 50.6564
payment_method: None 16.5, untouched
only NON_DIRECT_DEBIT rows 27.8488, untouched

Run with the fix: RESULTS: 7 passed, 0 failed out of 7 tests.

Run with octopus.py reverted to main, to show the test would have caught this:

ERROR: Expected 26.3814 at minute 0, got 27.8488
ERROR: Expected 50.6564 at minute 0, got 59.3136
RESULTS: 6 passed, 1 failed out of 7 tests

Note that the "direct debit row last" case passes even without the fix — that is the order-dependence itself.

Other checks

  • ./run_all --quick: the only failure is predheat ("Predheat did not run promptly after being re-enabled"), which fails the same way on a clean checkout of main on this machine (Windows), so it is unrelated to this change.
  • black --check on both files: unchanged.
  • flake8: same number of findings as main on octopus.py (all pre-existing).
  • interrogate: 90.4% on main → 90.6% with this change, for these two files.

🤖 Generated with Claude Code

…overlap

The REST tariff endpoints return a DIRECT_DEBIT row and a NON_DIRECT_DEBIT row
over the same validity window. Both went unfiltered into minute_data(), which
writes each row over its range, so the row appearing last in the response won.

Measured against the live public API for E-1R-VAR-22-11-01-A: predbat used
27.8488 where the direct debit rate is 26.381355, and 5 of the 17 windows
returned put DIRECT_DEBIT last, so the displayed variant was not even stable
between periods.

filter_payment_method() now runs at both parse points, the OctopusAPI component
path and the legacy import_octopus_url path. Rows with payment_method None
(Agile, day/night), rows without the key, and responses that never mention
DIRECT_DEBIT are all left untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flexible Octopus tariff parsing selects non-direct-debit rates

1 participant