cadrumo.domain.usage_ratios.model module¶
Pydantic model and resolver for the operator’s per-category usage-ratio overrides.
Defines UsageRatioProfile — the strict, frozen pydantic v2 record
that captures the operator’s persisted business / personal split coefficients
— plus resolve_user_ratio(), the pure helper consumed by
cadrumo.domain.deductibility to look up an override before falling back to
the statutory domain.categories.proportionality.ProportionalityRule.default_ratio.
The eligibility set ELIGIBLE_USAGE_RATIO_CATEGORIES is derived lazily
from the current pinned generation’s complete category-profile corpus, never
from one filing year. Each decoded profile retains only that immutable set so
later pure edits perform no hidden authority read. Eligibility gates what a
persisted profile may STORE, so a year-scoped set would invalidate an operator’s
stored overrides the moment the filing year rolled over. The year-versioned half – the statutory multiplier
and default ratio the law fixes per year – is read at use time from
domain.categories.registry.resolve_category_profiles(), which takes the year
explicitly.
- validate_usage_ratio_bound(ratio, *, label)[source]¶
Return
ratiowhen it lies in the closed interval[0, 1], else raise.The single authority for the usage-ratio range. Both the persisted
UsageRatioProfileand every transport surface that carries a usage ratio route their bound check through this function, so the interval is declared once rather than re-stated per call site.- Parameters:
- Return type:
- Returns:
The same
ratiowhen it is within[0, 1].- Raises:
UsageRatioValidationError – When
ratiofalls outside[0, 1].
- eligible_usage_ratio_categories()[source]¶
Return every category the shipped corpus makes eligible for a user ratio.
Read from the undated corpus rather than from any resolved filing year. A category the operator was permitted to override under one filing year must stay loadable under the next, and the proportionality kind is one of the framework facts that does not vary by year, so year selection would add a dependency this set must not have.
- Return type:
- ELIGIBLE_USAGE_RATIO_CATEGORIES: Set[SpendingCategory]¶
Categories for which a
UsageRatioProfilemay carry an override.Resolved on first use inside the calling operation’s pinned generation: a category is eligible iff its projected proportionality kind declares the usage-ratio evaluator role. The weak-owner cache cannot retain retired authority generations.
- class UsageRatioProfile(**data)[source]¶
Bases:
BaseModelThe operator’s persisted per-category usage-ratio overrides.
Only categories listed in
ELIGIBLE_USAGE_RATIO_CATEGORIESmay be persisted; every other key is rejected by the cross-field validator. The bounds validator additionally rejectsNaN/InfinityDecimal values and any ratio outside the inclusive[0, 1]range. Stored ratios are canonicalised by category-value sort order so two equal profiles serialise to identical bytes — a property relied on when the encrypted envelope is git-tracked.The inner mapping is frozen after validation. Callers use
with_ratio()/without_ratio()to derive new profiles.- Variables:
ratios – Frozen mapping from
domain.categories.spending_category.SpendingCategoryto aDecimalin[0, 1].- Parameters:
data (Any)
- ratios: Mapping[SpendingCategory, Decimal]¶
- with_ratio(category, ratio)[source]¶
Return a new
UsageRatioProfilewith one ratio set or replaced.- Parameters:
category (
SpendingCategory) – The spending category whose ratio to set.ratio (
Decimal) – The replacement ratio in[0, 1].
- Return type:
- Returns:
A fresh frozen profile; the receiver is left unchanged.
- without_ratio(category)[source]¶
Return a new
UsageRatioProfilewith one ratio removed.A no-op when
categoryhas no current override.- Parameters:
category (
SpendingCategory) – The spending category whose override to remove.- Return type:
- Returns:
A fresh frozen profile; the receiver is left unchanged.
- resolve_user_ratio(profile, category)[source]¶
Return the operator’s persisted ratio for
category, orNoneif unset.Pure helper consumed by
cadrumo.domain.deductibility. When the return value isNonethe caller falls back todomain.categories.proportionality.ProportionalityRule.default_ratioand records the resolution source in the transaction trace fields.- Parameters:
profile (
UsageRatioProfile) – The operator’s currently persistedUsageRatioProfile.category (
SpendingCategory) – The spending category whose ratio to resolve.
- Return type:
- Returns:
The user-configured ratio, or
Noneif the category has no override.
- class UsageRatioReference(**data)[source]¶
Bases:
BaseModelValidated reference from one ledger transaction to a usage-ratio profile entry.
- Parameters:
data (Any)
- usage_ratio_id: str¶
- category: SpendingCategory¶
- ratio: Decimal¶
- validate_usage_ratio_reference(profile, *, category_id, usage_ratio_id, business_pct=None)[source]¶
Validate a ledger transaction’s usage-ratio reference against
profile.Usage-ratio profiles are keyed by
SpendingCategory; therefore a persisted ledger reference must be the concrete category value, not a CLI alias or a parallel identifier. When a row also carriesbusiness_pct, the percentage must match the referenced profile ratio so the stored transaction fact and its proportionality source cannot drift.- Return type:
- Returns:
The validated
UsageRatioReferenceanchored to the profile.- Parameters:
profile (UsageRatioProfile)
category_id (str | None)
usage_ratio_id (str)
business_pct (Decimal | None)