feat: name the applied rate in the payout memo

A back-dated payout's sat figure is unexplainable on its own — employee_2's
August backfill paid 18,353 sat and 14,794 sat for the same ten euro, and
only the rate says why. Both wallets now show it:

    dev retainer — 2026-08-01 · 10 EUR @ 54,487 EUR/BTC
    dev retainer — 2026-08-29 · 10 EUR @ 67,596.6 EUR/BTC

This required moving the `current` mode conversion into payroll as well.
Previously that mode handed create_invoice a fiat amount and let LNbits
convert, so the rate only became knowable by reading it back off the
resulting invoice — after the memo had already been fixed. Now every mode
resolves its rate before invoicing and the memo is accurate in all three,
rather than only for the back-dated ones.

It remains a single conversion, not a second opinion: fiat_amount_as_satoshis
is the same function create_invoice would have called, and the rate is
derived by the same formula calculate_fiat_amounts uses. Because passing
sats means LNbits no longer stamps the fiat metadata itself, payroll now
writes the identical fiat_currency / fiat_amount / fiat_rate / btc_rate keys
onto the payment, so a payroll payment still reads like any other
fiat-priced one in the payments list.

A rate that rounds an amount to zero sats now fails the period rather than
raising a zero-division while deriving the rate.

Memo omits the rate clause for sat-denominated contracts, where there is no
conversion to report.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jy52j9GRZ6XKa1Zt21LLj
This commit is contained in:
Padreug 2026-08-31 21:55:49 +02:00
commit 3b14db3dff
4 changed files with 121 additions and 27 deletions

View file

@ -24,6 +24,7 @@ def make_contract(
created_at: str = "2026-01-01",
pricing_mode: PricingMode = PricingMode.payday,
manual_rate: float | None = None,
label: str = "",
) -> Contract:
return Contract(
id=contract_id,
@ -41,6 +42,7 @@ def make_contract(
backfill=backfill,
pricing_mode=pricing_mode,
manual_rate=manual_rate,
label=label,
created_at=datetime.strptime(created_at, "%Y-%m-%d").replace(
tzinfo=timezone.utc
),