Mercury Genesys Token Calculator
This commit is contained in:
151
calculators/Genesys_Token_Calculator/genesyscalc/billing.py
Normal file
151
calculators/Genesys_Token_Calculator/genesyscalc/billing.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""Tokens → dollars.
|
||||
|
||||
The order of operations is the whole content of this module, and it is
|
||||
pinned by tests because getting it wrong moves the bill:
|
||||
|
||||
1. Round consumption up to whole tokens, monthly. Per-user subscription
|
||||
totals are exact and are NOT rounded — only metered consumption is.
|
||||
2. Deduct the free monthly allowance (250 named / 350 concurrent).
|
||||
3. Floor at zero. The allowance renews monthly and "do[es] not carry over
|
||||
to future months", so an unused remainder is discarded, never banked.
|
||||
|
||||
Everything commercial — the contracted rate, the concession — arrives via
|
||||
:class:`genesyscalc.meters.TokenPrice` from the notebook's engagement-data
|
||||
cell. This module knows arithmetic, not deals.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .meters import Confidence, LicenceModel, TokenPrice
|
||||
from .ratecard import FREE_TOKENS_PER_MONTH, meter
|
||||
|
||||
MONTHS_PER_YEAR: int = 12
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CostLine:
|
||||
"""One feature's monthly and annual cost, ready to render."""
|
||||
|
||||
key: str
|
||||
feature: str
|
||||
units_monthly: float
|
||||
unit_label: str
|
||||
tokens_monthly: float
|
||||
cost_monthly: float
|
||||
cost_annual: float
|
||||
confidence: Confidence
|
||||
source_url: str | None
|
||||
note: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CostTotals:
|
||||
"""The token bill, with the allowance and the MSRP walk both visible."""
|
||||
|
||||
consumption_tokens_monthly: float
|
||||
subscription_tokens_monthly: float
|
||||
gross_tokens_monthly: float
|
||||
allowance_tokens_monthly: int
|
||||
billable_tokens_monthly: int
|
||||
effective_rate: float
|
||||
currency: str
|
||||
token_cost_monthly: float
|
||||
token_cost_annual: float
|
||||
list_cost_annual: float
|
||||
concession_saving_annual: float
|
||||
|
||||
|
||||
def billable_tokens_monthly(
|
||||
consumption_tokens: float,
|
||||
subscription_tokens: float,
|
||||
licence: LicenceModel,
|
||||
apply_allowance: bool = True,
|
||||
) -> int:
|
||||
"""Whole tokens actually billed in a month.
|
||||
|
||||
``ceil`` lands on the consumption subtotal (Genesys bills whole tokens);
|
||||
subscription totals are already whole and exact. The allowance is then
|
||||
deducted and the result floored at zero — it does not carry over, so an
|
||||
unused remainder is simply lost.
|
||||
"""
|
||||
if consumption_tokens < 0 or subscription_tokens < 0:
|
||||
raise ValueError("token counts must not be negative")
|
||||
gross = math.ceil(consumption_tokens) + subscription_tokens
|
||||
allowance = FREE_TOKENS_PER_MONTH[licence] if apply_allowance else 0
|
||||
return max(0, int(gross) - allowance)
|
||||
|
||||
|
||||
def allowance_for(licence: LicenceModel, apply_allowance: bool = True) -> int:
|
||||
"""The free monthly token allowance actually in play."""
|
||||
return FREE_TOKENS_PER_MONTH[licence] if apply_allowance else 0
|
||||
|
||||
|
||||
def cost_lines(
|
||||
tokens_by_key: dict[str, float], units_by_key: dict[str, float], price: TokenPrice
|
||||
) -> tuple[CostLine, ...]:
|
||||
"""One :class:`CostLine` per meter, in published order, at the effective rate.
|
||||
|
||||
Line costs are unrounded and un-allowanced: the allowance is an
|
||||
org-level deduction, not a per-feature one, so attributing it to a
|
||||
single line would misstate that feature's economics. It lands in
|
||||
:func:`total_cost`.
|
||||
"""
|
||||
rate = price.effective_rate()
|
||||
lines: list[CostLine] = []
|
||||
for key in sorted(tokens_by_key, key=lambda k: meter(k).feature):
|
||||
m = meter(key)
|
||||
tokens = tokens_by_key[key]
|
||||
monthly = tokens * rate
|
||||
lines.append(
|
||||
CostLine(
|
||||
key=key,
|
||||
feature=m.feature,
|
||||
units_monthly=units_by_key.get(key, 0.0),
|
||||
unit_label=m.unit_label,
|
||||
tokens_monthly=tokens,
|
||||
cost_monthly=monthly,
|
||||
cost_annual=monthly * MONTHS_PER_YEAR,
|
||||
confidence=m.confidence,
|
||||
source_url=m.source_url,
|
||||
note=m.note,
|
||||
)
|
||||
)
|
||||
return tuple(lines)
|
||||
|
||||
|
||||
def total_cost(
|
||||
consumption_tokens_monthly: float,
|
||||
subscription_tokens_monthly: float,
|
||||
licence: LicenceModel,
|
||||
price: TokenPrice,
|
||||
apply_allowance: bool = True,
|
||||
) -> CostTotals:
|
||||
"""The org-level bill: allowance deducted, MSRP walk carried alongside."""
|
||||
billable = billable_tokens_monthly(
|
||||
consumption_tokens_monthly,
|
||||
subscription_tokens_monthly,
|
||||
licence,
|
||||
apply_allowance,
|
||||
)
|
||||
rate = price.effective_rate()
|
||||
monthly = billable * rate
|
||||
annual = monthly * MONTHS_PER_YEAR
|
||||
list_annual = billable * price.list_rate * MONTHS_PER_YEAR
|
||||
return CostTotals(
|
||||
consumption_tokens_monthly=consumption_tokens_monthly,
|
||||
subscription_tokens_monthly=subscription_tokens_monthly,
|
||||
gross_tokens_monthly=(
|
||||
math.ceil(consumption_tokens_monthly) + subscription_tokens_monthly
|
||||
),
|
||||
allowance_tokens_monthly=allowance_for(licence, apply_allowance),
|
||||
billable_tokens_monthly=billable,
|
||||
effective_rate=rate,
|
||||
currency=price.currency,
|
||||
token_cost_monthly=monthly,
|
||||
token_cost_annual=annual,
|
||||
list_cost_annual=list_annual,
|
||||
concession_saving_annual=list_annual - annual,
|
||||
)
|
||||
Reference in New Issue
Block a user