cadrumo.application.aggregation._retencion_rate_advisory module

Statutory-rate advisories for retención figures the engine did not compute.

Two surfaces live here, one per side of the retención relationship, because both ask the same question — does this withheld figure correspond to a rate the law actually fixes? — and answering it in one place keeps the comparison, its cent tolerance, and its grounding from being restated twice.

The RECEIVED side (administrador_retencion_rate_advisory_observations()) screens operator-entered per-perceptor rows. The ISSUED side (inferred_actividad_retencion_rate_advisory_observations()) screens an activity-ledger withholding inferred from a cash shortfall.

Administrador/consejero rows (RECEIVED side)

The received surface aggregates operator-supplied per-perceptor rows: each RetencionObservation carries both the taxable_base and the withheld retencion_amount. For ordinary work-income rows the withholding is a personalised progressive computation, so no single rate can be asserted. Administrator rows carry the fixed-treatment scheme selected by the governing registry when the paying entity’s turnover condition requires it.

The engine does not compute the withheld amount (the operator enters it from their payroll), so the fixed rate could previously go unverified: an administrador row carrying, say, the ordinary empleado rate would fold into the trabajo block and file silently. This module surfaces that as a non-blocking CalculationSourceDiagnostic on the calculate path, grounded in the registry-backed load_administrador_retencion_rates() rate set (no-silent-under-declaration). The rate figures and their legal grounding are regulatory data read from the registry, never literals in this or the core layer (aeat-registry-authority-flow). Because the engine cannot always know the paying entity’s INCN, a row whose effective rate matches either authority-selected figure is treated as conforming; only a row consistent with neither raises the advisory.

Inferred actividad-económica retención (ISSUED side)

The activity income ledger derives retención practicada as the declared invoice gross minus the cash actually received, bounded ABOVE by the governed activity rate. Nothing bounds it BELOW, and nothing can: a shortfall is a shortfall whatever caused it. So a cash gap arising from something that is not a retención at all — a correspondent-bank fee on a foreign transfer, a rounding short-pay, a pronto-pago discount agreed after invoicing, a line the client disputed and deducted — lands in the same subtraction and becomes a pago-a-cuenta credit for tax nobody withheld and AEAT never received. It then settles against a payer record that reports nothing.

The inference itself is not the defect and is deliberately left alone: requiring a declared retención was considered and rejected, because the declared-first branch is not always reachable and dropping the inference would under-declare a credit the taxpayer is genuinely owed. What was missing is that the inference was invisible — the withheld_derivation marker recording that a figure was inferred rather than declared reached no operator surface at all.

The discriminator is that a real retención is a base × governed rate. Screening on the rate rather than on the derivation marker is what keeps this advisory off the correct domestic-B2B majority, where the shortfall genuinely IS the retención — a blanket advisory on every inference would fire on those and train operators to ignore the channel, the failure mode aeat-ledger-contract exists to prevent.

The comparison reads the complete governed rate set and its classifications from registry facts, never a literal restated here. This keeps additional sectoral pairings visible to the advisory instead of treating them as unmatched.

A match is not one verdict, because the authority classifications differ in how easily an accidental shortfall can reach them. A sectoral-only match therefore raises the separate inferred_retencion_sectoral_rate_unconfirmed reason code; “matches nothing” and “matches a weaker classification” are distinct machine states and must not be collapsed.

The active profile only words the sectoral advisory and never gates it. A weak signal may set how confidently we speak; it may never decide whether we speak.

The separate sectoral reason code keeps weaker classification matches visible instead of allowing them to pass silently.

The residual limit remains: a strong authority-rate match is indistinguishable from a real withholding without a declared source, so it passes silently.

Core types: TaxpayerProfile.

ADMINISTRADOR_RETENCION_RATE_SOURCE_KIND

Diagnostic source_kind for a fixed-rate administrator withholding whose amount matches neither authority-selected rate.

INFERRED_ACTIVIDAD_RETENCION_RATE_SOURCE_KIND

Diagnostic source_kind for an inferred activity withholding whose figure matches no authority-selected rate.

INFERRED_SECTORAL_RETENCION_RATE_SOURCE_KIND

Diagnostic source_kind for an INFERRED retención matching ONLY a sectoral rate — a weaker claim than the unmatched one, carried on its own kind so an automated operator routes on the field rather than on the prose.

administrador_retencion_rate_advisory_observations(observations, *, effective_date)[source]

Return advisories for administrator rows inconsistent with authority rates.

A CalculationSourceDiagnostic (reason administrador_retencion_rate_mismatch) is emitted for each registry selected fixed-treatment observation with a strictly-positive taxable_base whose withheld retencion_amount matches neither authority-selected fixed rate. Rows on any other scheme and administrator rows with a non-positive base are out of scope and never fire.

Parameters:
  • observations (Iterable[RetencionObservation]) – The per-perceptor retención rows feeding the calculation.

  • effective_date (date) – Endpoint of the actual filing period whose legal rates apply.

Return type:

tuple[CalculationSourceDiagnostic, ...]

Returns:

A tuple of non-blocking rate-mismatch diagnostics, in input order.

inferred_actividad_retencion_rate_advisory_observations(observations, *, bucket_id=None, profile_decode_context=None, resolver_id=None, profile=None)[source]

Return advisories for inferred retención against governed activity rates.

Three outcomes, decided by arithmetic alone against the governed rate set, for each row whose withheld_derivation says the figure was INFERRED from a cash shortfall and whose taxable_base_amount is positive. Rows carrying a retención DECLARED on a linked invoice, rows carrying no retención, and rows with no positive base never fire.

  • A professional-rate match is silent when the authority classifies it as a strong claim.

  • A sectoral-only match raises an inferred_retencion_sectoral_rate_unconfirmed advisory because its evidential strength is weaker.

  • The amount matches nothing — a inferred_retencion_rate_unmatched advisory, the strong finding.

bucket_id is read only to word the sectoral message, never to decide whether it fires. A weak profile signal may shape confidence but cannot gate the diagnostic.

The rate set is read from governed facts via statutory_activity_retencion_rates(), so the comparison tracks the grounded legal figures rather than a literal restated here, and a newly-grounded rate widens the conforming band automatically.

Parameters:
  • observations (Iterable[RentaIncomeObservation]) – The actividad-económica income rows feeding the calculation.

  • bucket_id (str | None) – Bucket whose active profile words the sectoral advisory.

  • profile_decode_context (ProfileDecodeContext | None) – Pinned schema context used when reading that profile; absence keeps the wording explicitly unconfirmed.

  • resolver_id (str | None) – Identifier of the resolver emitting these diagnostics, stamped onto each one so the operator can attribute an advisory to the source that raised it. Every sibling diagnostic builder in the calculate mesh carries it; omitting it here delivered these two advisories with a null attribution while their neighbours were attributed. None (or an absent/unreadable profile) simply yields the could-not-be-checked wording; it never suppresses a diagnostic.

  • profile (ModeloWorkProfile | None) – The bucket’s profile when the calculation already loaded it; when omitted, the wording reads it from bucket_id.

Return type:

tuple[CalculationSourceDiagnostic, …]

Per row rather than aggregated, unlike the ungrounded-substrate advisory: the firing set is meant to be small and the actionable unit is one transaction whose cash gap needs explaining, so the transaction id is the payload.

Return type:

tuple[CalculationSourceDiagnostic, ...]

Returns:

A tuple of non-blocking rate diagnostics, in input order.

Parameters: