fix: honour a stated rate whatever the calendar says
Pay-now on a period due next week offered "Rate on the payday" and then found a rate anyway, with only a passive hint to explain why. Two separate problems behind that. The real one: `resolve_price` tested `payday >= today` before it tested the mode, so `manual` was silently ignored for any period not already back-dated. "Pay at the rate we agreed" quietly did not, and a contract pegged to a fixed rate honoured it only on late periods. A stated rate is an instruction rather than a lookup, so it now wins outright and applies to every period. `payday` and `current` keep their old order — for a payday today or ahead there is no history to consult, so `payday` collapses into `current`, which is the only defensible answer for a date that has not happened. The cosmetic one: the dialog's hint said "ignored unless the payday is already in the past", sitting beside a control that plainly appeared to do something — and was about to become wrong anyway, since manual now always applies. It is replaced by a line that names the actual period: either "Payday 2026-09-05 has not passed, so there is no historical rate to look up — this will be priced at the current rate", or "Will convert at what BTC was worth on <date>". The dialog header now shows which payday is being settled, which was not visible at all before. The contract dialogs lose the same stale hint; their label becomes "Pricing" rather than "Price a late payday at", which no longer describes manual mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jy52j9GRZ6XKa1Zt21LLj
This commit is contained in:
parent
3b14db3dff
commit
1584eb337f
5 changed files with 94 additions and 19 deletions
33
services.py
33
services.py
|
|
@ -290,12 +290,26 @@ async def resolve_price(
|
|||
mode = mode or contract.pricing_mode
|
||||
manual_rate = manual_rate or contract.manual_rate
|
||||
|
||||
# A payday that is today or ahead needs no history: every mode agrees on
|
||||
# the live rate. Payroll still does the conversion rather than handing
|
||||
# create_invoice a fiat amount, because knowing the rate *before* the
|
||||
# invoice exists is what lets the memo state it. It is the same single
|
||||
# conversion either way — fiat_amount_as_satoshis is the function
|
||||
# create_invoice would have called — not a second opinion.
|
||||
# An explicit rate wins outright, whatever the calendar says. It is an
|
||||
# instruction rather than a lookup — "pay at the rate we agreed" is as
|
||||
# meaningful for a payday next week as for one last month, and a
|
||||
# contract pegged to a fixed rate should honour it on every period.
|
||||
if mode == PricingMode.manual:
|
||||
if not manual_rate:
|
||||
raise PricingError("manual pricing selected but no rate given")
|
||||
return Price(
|
||||
sats_for(contract.amount, manual_rate), "sat", manual_rate, "manual"
|
||||
)
|
||||
|
||||
# A payday that is today or ahead has no history to consult, so `payday`
|
||||
# mode collapses into `current` — a future rate is not knowable, and the
|
||||
# live one is the only defensible answer.
|
||||
#
|
||||
# Payroll still does the conversion rather than handing create_invoice a
|
||||
# fiat amount, because knowing the rate *before* the invoice exists is
|
||||
# what lets the memo state it. Same single conversion either way —
|
||||
# fiat_amount_as_satoshis is the function create_invoice would have
|
||||
# called — not a second opinion.
|
||||
if payday >= today or mode == PricingMode.current:
|
||||
amount_sat = await fiat_amount_as_satoshis(contract.amount, contract.currency)
|
||||
if amount_sat <= 0:
|
||||
|
|
@ -306,13 +320,6 @@ async def resolve_price(
|
|||
rate = (contract.amount / amount_sat) * 100_000_000
|
||||
return Price(amount_sat, "sat", rate, "current")
|
||||
|
||||
if mode == PricingMode.manual:
|
||||
if not manual_rate:
|
||||
raise PricingError("manual pricing selected but no rate given")
|
||||
return Price(
|
||||
sats_for(contract.amount, manual_rate), "sat", manual_rate, "manual"
|
||||
)
|
||||
|
||||
historical = await historical_btc_rate(payday, contract.currency)
|
||||
if historical is None:
|
||||
raise PricingError(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue