aeat.domain.usage_ratios._service module

Pure usage-ratio register logic: object key, mutation lock, censo derivation.

Usage ratios carry business / personal split percentages. The encrypted Envelope-wrapped persistence lives in the adapter (adapters.persistence.profile.usage_ratios); this module owns only the persistence-substrate-free pieces: the secure-object key convention, the per-bucket read-modify-write lock, and the censo-derived HOME_OFFICE ratio computation.

usage_ratios_object_key(bucket_id)[source]

Return the secure object key for one profile bucket’s usage-ratio profile.

Return type:

str

Parameters:

bucket_id (str)

usage_ratio_bucket_lock(bucket_id)[source]

Serialise the load-modify-save of one bucket’s usage-ratio row.

The usage-ratio profile is a single SecureObjectRepository row per bucket. Every mutator (ratios set / ratios unset and the censo home-office seed) reads that row, mutates one entry, and writes the whole row back. Without a cross-transaction guard two concurrent writers each load the same snapshot, apply their own change, and the second save silently drops the first writer’s category — a lost update. The encrypted-SQLite busy_timeout / WAL only serialises individual write transactions; it does not make the read-modify-write span atomic, so the lost update persists.

This context manager acquires the project’s OS-level core.locks.exclusive_file_lock() on a per-bucket sidecar so the whole load-modify-save runs under mutual exclusion. Because the lock is held across both the load and the save, concurrent writers serialise and every update survives — there is no silent overwrite. The lock is scoped to the bucket, so writers on unrelated profiles never contend.

Parameters:

bucket_id (str) – Profile bucket whose usage-ratio row is being mutated.

Raises:

LockAcquisitionError – When the per-bucket lock cannot be acquired within the configured aeat_file_lock_timeout_s budget.

Return type:

Iterator[None]

derive_home_office_ratios_from_censo(raw_afectacion_ratio, *, year)[source]

Build a UsageRatioProfile for HOME_OFFICE categories from the censo.

The operator’s vivienda afectación ratio is the raw office_m2 / total_m2 computed from the AEAT-bound censo facts (LIRPF Art. 30.2 rule 5, Ley 6/2017 BOE-A-2017-12544). For every HOME_OFFICE_SUMINISTROS category, the ratio is multiplied by the registry rule’s statutory_multiplier (legally 0.30 for utility costs); for every HOME_OFFICE_OWNERSHIP category, the multiplier is absent (effective factor 1.0) so the operator-chosen ratio is the full deductible percentage.

Parameters:
  • raw_afectacion_ratio (Decimal) – office_m2 / total_m2 as a Decimal in [0, 1]. Higher values are rejected; AEAT exclusive-use criteria forbid 100% afectación on the habitual vivienda.

  • year (int) – Registry profile year (e.g. 2025) whose proportionality rules drive the derivation.

Return type:

UsageRatioProfile

Returns:

A UsageRatioProfile carrying one entry per HOME_OFFICE category, each set to its legally-effective deductible percentage.

Raises:

UsageRatioValidationError – When raw_afectacion_ratio is outside the [0, 1] range.