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 ratio when it lies in the closed interval [0, 1], else raise.

The single authority for the usage-ratio range. Both the persisted UsageRatioProfile and 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:
  • ratio (Decimal) – Candidate usage ratio.

  • label (str) – Human-readable subject named in the refusal (a category id for the persisted profile, a field name at a transport edge).

Return type:

Decimal

Returns:

The same ratio when it is within [0, 1].

Raises:

UsageRatioValidationError – When ratio falls 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:

frozenset[SpendingCategory]

ELIGIBLE_USAGE_RATIO_CATEGORIES: Set[SpendingCategory]

Categories for which a UsageRatioProfile may 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: BaseModel

The operator’s persisted per-category usage-ratio overrides.

Only categories listed in ELIGIBLE_USAGE_RATIO_CATEGORIES may be persisted; every other key is rejected by the cross-field validator. The bounds validator additionally rejects NaN / Infinity Decimal 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.SpendingCategory to a Decimal in [0, 1].

Parameters:

data (Any)

ratios: Mapping[SpendingCategory, Decimal]
with_ratio(category, ratio)[source]

Return a new UsageRatioProfile with 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:

UsageRatioProfile

Returns:

A fresh frozen profile; the receiver is left unchanged.

without_ratio(category)[source]

Return a new UsageRatioProfile with one ratio removed.

A no-op when category has no current override.

Parameters:

category (SpendingCategory) – The spending category whose override to remove.

Return type:

UsageRatioProfile

Returns:

A fresh frozen profile; the receiver is left unchanged.

resolve_user_ratio(profile, category)[source]

Return the operator’s persisted ratio for category, or None if unset.

Pure helper consumed by cadrumo.domain.deductibility. When the return value is None the caller falls back to domain.categories.proportionality.ProportionalityRule.default_ratio and records the resolution source in the transaction trace fields.

Parameters:
Return type:

Decimal | None

Returns:

The user-configured ratio, or None if the category has no override.

class UsageRatioReference(**data)[source]

Bases: BaseModel

Validated 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 carries business_pct, the percentage must match the referenced profile ratio so the stored transaction fact and its proportionality source cannot drift.

Return type:

UsageRatioReference

Returns:

The validated UsageRatioReference anchored to the profile.

Parameters: