cadrumo.domain.iva_compensation.reconciliation module

IVA compensation reconciliation decision data models.

The live AEAT wallet is external state. Local Modelo 303 recurrence is internal reconstruction. These pure typed records describe the evidence sources, the explicit taxpayer override, and the effective binding decision consumed by Modelo 303 calculation. They carry no adapter or application coupling: the orchestration that produces them from live wallet observations and prior-filing recurrence stays in the application layer.

The IvaCompensationReconciliationDecision model encodes the regulatory invariant relating selected_authority to selected_amount and the blocked-wallet refusal in its model_validator.

class IvaCompensationDecisionReason(*values)[source]

Bases: StrEnum

Locale-neutral identity explaining an IVA compensation decision.

TAXPAYER_OVERRIDE
FIRST_PERIOD_ZERO_AEAT_WALLET
FIRST_PERIOD_ZERO_ACTIVITY_START_UNCONTRASTED
FIRST_PERIOD_ZERO_LOCAL_RECURRENCE
LOCAL_EVIDENCE_UNREADABLE
NO_USABLE_AUTHORITY
FILED_HISTORY_ZERO
FILED_HISTORY_REQUIRES_OVERRIDE
LOCAL_RECURRENCE_ZERO
LOCAL_RECURRENCE_REQUIRES_OVERRIDE
STALE_WALLET_NO_LOCAL_RECURRENCE
STALE_WALLET_LOCAL_RECURRENCE_REQUIRES_OVERRIDE
WALLET_LOCAL_RECURRENCE_DIVERGENCE
AEAT_WALLET_VALIDATED
AEAT_WALLET_UNCROSSCHECKED
CALLER_ZERO_MATCHES_LOCAL_AUTHORITY
class IvaCompensationOverride(**data)[source]

Bases: BaseModel

Explicit taxpayer override for Modelo 303 prior compensation.

Parameters:

data (Any)

amount: Decimal
operator_explanation: str
evidence_locator: str
recorded_at: UtcInstant
class IvaCompensationAuthoritySource(**data)[source]

Bases: BaseModel

One evidence source considered by an IVA compensation decision.

Parameters:

data (Any)

source_kind: IvaCompensationAuthorityKind
amount: Decimal | None
source_locator: str
captured_at: UtcInstant | None
source_modelo: str | None
source_filing_year: FilingYear | None
source_periods: tuple[Period, ...]
registry_snapshot_refs: tuple[RegistrySnapshotRef, ...]
class IvaCompensationReconciliationDecision(**data)[source]

Bases: BaseModel

Effective prior-compensation binding decision for Modelo 303.

This record is persisted and replayed by calculation. A live wallet fetch creates evidence; this decision says whether that evidence can safely drive the binding or whether review is required.

Parameters:

data (Any)

taxpayer_nif: str
target_year: FilingYear
target_period: Period
target_registry_snapshot_ref: RegistrySnapshotRef
source_registry_snapshot_refs: tuple[RegistrySnapshotRef, ...]
selected_authority: IvaCompensationAuthority
selected_amount: Decimal | None
wallet_amount: Decimal | None
local_recurrence_amount: Decimal | None
override_amount: Decimal | None
divergence: IvaCompensationDivergence
blocked: bool
stale_wallet: bool
reason_identity: IvaCompensationDecisionReason
operator_explanation: str | None
local_evidence_found_but_unusable: bool

Whether the caller found a prior local record and could not read it as prior-compensation evidence. Both that situation and a genuinely absent record leave every other field on this model identical – same divergence, same absent amounts, same blocked – so without this the difference exists only inside reason’s free text and no consumer can key on it. A refusal that wants to tell an operator which one happened has nothing else to read.

wallet_captured_at: UtcInstant | None
authority_sources: tuple[IvaCompensationAuthoritySource, ...]
decided_at: UtcInstant
class IvaCompensationWalletObservationProtocol(*args, **kwargs)[source]

Bases: Protocol

Structural port for an AEAT IVA-compensation wallet observation.

Lets the reconciliation decision logic stay in the domain without importing the Sede adapter record that produces it; the adapter’s IvaCompensationWalletObservation satisfies this protocol structurally. Members are read-only (the reconciliation only reads them), which keeps the protocol covariant so a record whose attributes are subtypes (e.g. an AnyHttpUrl source_url) still satisfies it.

property taxpayer_nif: str

The NIF the wallet observation was captured for.

property target_year: int

The filing year the pending balance is carried into.

property target_period: Period

The period the pending balance is carried into.

property total_pending: Decimal

The cuota AEAT still shows as pending compensation.

property source_url: object

Where on the sede the observation was read from.

property captured_at: datetime

When the observation was taken.

class LocalIvaCompensationRecurrenceProtocol(*args, **kwargs)[source]

Bases: Protocol

Structural port for a local Modelo 303 recurrence record.

The application’s LocalIvaCompensationRecurrence satisfies this protocol structurally; the domain projects it into an authority source without importing the application layer. Members are read-only for covariance.

property amount: Decimal

The recurrence amount the local record carries.

property binding_id: object

The binding this recurrence resolves for.

property source_kind: str

Which source family produced the recurrence.

property source_modelo: object

The modelo the recurrence was derived from.

property source_filing_year: int

The filing year the source belongs to.

property source_periods: tuple[Period, ...]

The periods the source covers.

property source_registry_snapshot_refs: tuple[RegistrySnapshotRef, ...]

Canonical registry coordinates that produced the recurrence.

property resolved_at: datetime

When the recurrence was resolved.

reconcile_iva_compensation_wallet(*, taxpayer_nif, target_year, target_period, target_registry_snapshot_ref, wallet, local_recurrence_amount, local_recurrence_source=None, override=None, decided_at=None, max_wallet_age_days=31, is_first_iva_period=False, local_evidence_found_but_unusable=False)[source]

Return the IvaCompensationReconciliationDecision for casilla 110.

local_evidence_found_but_unusable is a caller-asserted fact, in the same shape as is_first_iva_period: the caller knows whether it FOUND a prior record and failed to interpret it, and this function cannot derive that from an absent amount. Without it the no-authority outcome states that nothing is available, which is false for that caller and sends an operator looking for evidence they already hold.

Return type:

IvaCompensationReconciliationDecision

Parameters:
local_recurrence_authority_source(recurrence)[source]

Project a local Modelo 303 recurrence record into an IvaCompensationAuthoritySource.

Return type:

IvaCompensationAuthoritySource | None

Parameters:

recurrence (LocalIvaCompensationRecurrenceProtocol | None)

validate_wallet_matches_snapshot(wallet, *, taxpayer_nif, target_year, target_period)[source]

Refuse a wallet observation that does not match the requested Modelo 303 target.

Return type:

None

Parameters: