cadrumo.application.ledger.ratios module

Operator-facing extensions for the aeat app ledger ratios verb-group.

The existing usage-ratio CRUD verbs (list/set/unset) live in the application-layer ledger actions backed by the domain usage_ratios module. This module adds two read-only verbs that close the discoverability and pre-calculate readiness gaps:

eligible enumerate categories that may carry a user ratio,

annotated with their statutory default ratio

validate inspect the persisted profile against eligibility and

bound rules; report missing categories per modelo

class EligibleCategoryRow(**data)[source]

Bases: BaseModel

One row in the ratios eligible listing.

default_ratio is None for eligible categories whose proportionality rule does not ship a statutory default; the operator must supply an override before the modelo pre-calculate readiness check passes.

Parameters:

data (Any)

category: SpendingCategory
proportionality_kind: ProportionalityKind
default_ratio: Decimal | None
override_present: bool
class RatiosValidationFinding(**data)[source]

Bases: BaseModel

One issue raised by ratios validate for an eligible category.

Parameters:

data (Any)

category: SpendingCategory
kind: str
detail: _FindingDetail
class RatiosValidationReport(**data)[source]

Bases: BaseModel

Result of ratios validate. Read-only, emits no bucket event.

Parameters:

data (Any)

bucket_id: BucketId
profile_present: bool
eligible_count: NonNegativeInt
overrides_count: NonNegativeInt
missing_overrides: tuple[SpendingCategory, ...]
findings: tuple[RatiosValidationFinding, ...]
eligible_ratio_categories(profile, *, year, operation)[source]

Return all eligible categories with their default ratio + override flag.

The output is sorted by canonical category value so callers can diff snapshots without ordering noise. Each row is an EligibleCategoryRow.

Parameters:
  • profile (UsageRatioProfile) – The operator’s persisted per-category overrides.

  • year (int) – Filing year whose category profiles supply the statutory default ratio. Required rather than defaulted: the default ratio is year-versioned regulatory data, and a pinned year would apply one year’s law to every filing.

  • operation (PinnedAuthorityOperation) – The caller-owned authority generation used for every category and governed-fact query in this projection.

Return type:

tuple[EligibleCategoryRow, ...]

validate_ratios_profile(*, bucket_id, profile, operation, require_overrides_for=())[source]

Inspect a profile against eligibility and any required overrides.

require_overrides_for is the set of categories the caller needs explicitly populated (e.g. a modelo’s pre-calculate readiness check). Categories absent from ELIGIBLE_USAGE_RATIO_CATEGORIES raise a not_eligible finding rather than silently passing.

Returns a RatiosValidationReport.

Return type:

RatiosValidationReport

Parameters:
list_eligible_ratios_for_bucket(*, bucket_id, year, operation)[source]

Convenience: load the bucket’s profile and project the eligibility report.

Each element is an EligibleCategoryRow describing one spending category’s eligibility and configured ratio.

Parameters:
  • bucket_id (str) – The bucket whose persisted profile is projected.

  • year (int) – Filing year whose category profiles supply the statutory default ratio.

  • operation (PinnedAuthorityOperation) – The caller-owned authority generation used to decode the stored profile.

Return type:

tuple[EligibleCategoryRow, ...]

validate_ratios_for_bucket(*, bucket_id, operation, require_overrides_for=())[source]

Load the bucket’s profile, run validation, and return a RatiosValidationReport.

Return type:

RatiosValidationReport

Parameters:
set_usage_ratio(*, bucket_id, category, ratio, operation)[source]

Set or replace one per-category usage-ratio override on the bucket.

Loads the bucket’s UsageRatioProfile, applies the override through the domain with_ratio validator, and persists the result. Returns the prior override value for the category (None when there was none) so the caller can emit a before/after audit event. Application command boundary for the CLI ledger ratios set verb; the CLI no longer calls the domain load/save primitives directly.

The load-modify-save runs under the per-bucket cadrumo.domain.usage_ratios.service.usage_ratio_bucket_lock() so two concurrent writers cannot read the same snapshot and lose one another’s override.

Return type:

Decimal | None

Parameters:
unset_usage_ratio(*, bucket_id, category, operation)[source]

Clear one per-category usage-ratio override on the bucket.

Returns the cleared value. Raises UsageRatioValidationError when the category carries no persisted override (so the caller can surface a precise “nothing to clear” message). Application command boundary for the CLI ledger ratios unset verb.

The load-modify-save runs under the per-bucket cadrumo.domain.usage_ratios.service.usage_ratio_bucket_lock() so a concurrent set on a sibling category cannot be lost by this clear.

Return type:

Decimal | None

Parameters:
class RatiosCensoOverrideWarning(**data)[source]

Bases: BaseModel

A non-fatal warning that the operator’s per-category override deviates from the censo-derived value.

The censo is the binding legal source of truth for censo-derived ratios. Operators may still override (e.g. to model a planned afectación change), but the engine emits a typed warning so downstream auditors can review the divergence.

Parameters:

data (Any)

category: SpendingCategory
override_ratio: UnitProportion
censo_derived_ratio: UnitProportion
raw_afectacion_ratio: UnitProportion
censo_business_pct_for(category, raw_afectacion_ratio, *, year, operation)[source]

Return the legally-effective business_pct for a category from censo.

The per-category projection of cadrumo.domain.usage_ratios.service.derive_home_office_ratios_from_censo(): given a single SpendingCategory and the operator’s bound censo office_m2 / total_m2, returns the raw_afectacion_ratio * statutory_multiplier value the classify and allocate paths should stamp onto Transaction.business_pct when no operator override is present. Returns None for categories outside the HOME_OFFICE families or when no censo has been applied yet, signalling to the caller that the operator’s explicit value (or the registry default) governs instead.

Return type:

Decimal | None

Parameters:
class BusinessSharePctOutcome(*values)[source]

Bases: StrEnum

Why a row did or did not receive a censo-derived business share.

Five members because the four ways of NOT deriving one are different facts about the taxpayer, and a caller that could only see None had to guess which. Only two of them are anything the operator can act on.

STATED

The operator supplied a share; no profile fact is consulted.

DERIVED_FROM_CENSO

The censo fact and the category’s statutory rule produced one.

NO_CATEGORY

No category, so there is nothing to apportion against.

NO_CENSO_APPLIED

No censo has been applied yet, so the afectación ratio is unknown.

CATEGORY_NOT_APPORTIONED

The category is outside the home-office families and is never apportioned by censo, whatever the taxpayer declared.

class BusinessSharePctResolution(**data)[source]

Bases: BaseModel

One resolution: what decided the share, and the share if there is one.

Parameters:

data (Any)

outcome: BusinessSharePctOutcome
business_pct: Decimal | None
resolve_business_share_pct(*, operator_supplied, category, censo_afectacion_ratio, year, operation)[source]

Decide the business share a row is stamped with, and say why.

The legal arithmetic is censo_business_pct_for(); this is the precedence around it, which was a chain of early returns at a command boundary and is therefore the half a second frontend had to reproduce.

The order is the rule. An operator statement wins over every profile fact, because it is the specific claim and the censo is only ever a default. Absence of a category is asked before absence of a censo: a row with no category has nothing to apportion whether or not a censo exists, and reporting the censo as the obstacle would send the operator to fix the wrong thing.

censo_afectacion_ratio is passed in rather than read here so this stays a decision about facts. WHICH profile is active, and whether one is active at all, is a session question the caller already answers.

Parameters:
  • operator_supplied (Decimal | None) – The share the operator stated, or None.

  • category (SpendingCategory | None) – The row’s spending category, or None when unset.

  • censo_afectacion_ratio (Decimal | None) – The bound office_m2 / total_m2 from the applied censo, or None when none has been applied.

  • year (int) – The filing year whose category profiles supply the statutory multiplier. Year-versioned regulatory data, so it comes from the row’s own booked date rather than a pinned literal.

  • operation (PinnedAuthorityOperation) – The caller-owned authority generation used for category family and proportionality resolution.

Return type:

BusinessSharePctResolution

Returns:

The resolution, carrying a share only where one was decided.

censo_override_warning(*, category, override_ratio, raw_afectacion_ratio, year, operation)[source]

Return a typed warning when an override deviates from the censo.

The check is silent for non-HOME_OFFICE categories: only the suministros and ownership home-office families are legally bound to the censo-derived afectación ratio (LIRPF Art. 30.2 rule 5, Ley 6/2017 BOE-A-2017-12544). For HOME_OFFICE categories the helper computes the legally-effective ratio (raw afectación times the rule’s statutory_multiplier) and compares it against override_ratio for exact equality. A non-equal pair returns a RatiosCensoOverrideWarning; equal values (and non-home-office categories) return None.

Parameters:
  • category (SpendingCategory) – The category being overridden via ratios set.

  • override_ratio (Decimal) – The operator-supplied override.

  • raw_afectacion_ratio (Decimal) – office_m2 / total_m2 from the bound censo snapshot.

  • year (int) – Registry year whose proportionality rule drives the derivation.

  • operation (PinnedAuthorityOperation) – The caller-owned authority generation used for category family and proportionality resolution.

Return type:

RatiosCensoOverrideWarning | None

Returns:

A RatiosCensoOverrideWarning if a warning should be emitted, otherwise None.

class UsageRatioMutationOutcomeV1(**data)[source]

Bases: BaseModel

What one ratio mutation changed, and what it raised against the Censo.

Parameters:

data (Any)

category: SpendingCategory
prior_ratio: Decimal | None
new_ratio: Decimal | None
censo_override_warning: RatiosCensoOverrideWarning | None
apply_usage_ratio_override(*, bucket_id, category, ratio, year, profile_id=None, raw_afectacion_ratio=None, operation)[source]

Persist one override, record it, and check it against the Censo.

Three things that had to happen together and were composed by the caller: the write, its audit event, and the comparison against the afectación ratio the operator declared to the AEAT. A surface that performed the write and forgot the comparison would let an override silently contradict the Censo.

NOT ATOMIC, and deliberately reported rather than papered over: the ratio store and the event history are separate secure objects with no shared commit, so a failure after the write leaves a persisted override with no audit event. Making it atomic needs the co-commit treatment the invoice link writer uses; this function does not invent one.

Parameters:
  • bucket_id (str) – The owning profile bucket.

  • category (SpendingCategory) – The spending category being overridden.

  • ratio (Decimal) – The override to store.

  • year (int) – The filing year the Censo comparison is made for.

  • profile_id (str | None) – The active profile, when one is bound.

  • raw_afectacion_ratio (Decimal | None) – The Censo-declared afectación ratio, when known.

  • operation (PinnedAuthorityOperation) – The caller-owned authority generation used to decode the stored profile.

Return type:

UsageRatioMutationOutcomeV1

Returns:

The prior and new values, plus any Censo override warning raised.

clear_usage_ratio_override(*, bucket_id, category, operation)[source]

Clear one override and record the clearance.

Carries the same non-atomicity as apply_usage_ratio_override().

Parameters:
  • bucket_id (str) – The owning profile bucket.

  • category (SpendingCategory) – The category whose override is cleared.

  • operation (PinnedAuthorityOperation) – The caller-owned authority generation used to decode the stored profile.

Return type:

UsageRatioMutationOutcomeV1

Returns:

The cleared value as prior_ratio, with new_ratio absent.

Raises:

UsageRatioValidationError – When the category carries no override.