cadrumo.application.live.filed_observation_persistence module

Persist filed AEAT observations into calculation-history repositories.

Filed Sede rows are promoted to registry-grounded CasillaObservation records in the official observation layer, and every matching justificante is reconciled with the period’s filing chain through the single reconciliation service; nothing here stamps AEAT acceptance itself.

The module treats AEAT live captures as official external evidence only after the captured justificante matches the filed observation. An all-numeric casilla manifest lets the reconciliation compare or record AEAT’s content; justificante metadata alone never claims casilla completeness.

See also

ExternalEvidenceKind

Closed evidence-kind catalogue; live captures stamp AEAT_LIVE_CAPTURE.

cadrumo.application.calculations.observations_repository.CalculationObservationRepositoryProtocol

Repository that receives the registry-grounded filed-declaration observations consumed by cross-period resolvers.

class FiledJustificanteUnreachedReason(*values)[source]

Bases: StrEnum

Why one stored justificante artefact produced no evidence.

Every member is a distinct dead end that used to share one shape: a log line plus None. A capture that extracted casillas while enrolling no justificante therefore reported an unexplained zero, which reads the same as a period with no receipt to enroll.

Variables:
  • UNREADABLE_ARTEFACT – Secure storage could not return the bytes.

  • MANIFEST_MISMATCH – The bytes disagree with the recorded length or digest.

  • UNPARSABLE_PDF – The bytes are not a receipt this parser can read.

  • CSV_UNRESOLVABLE – The artefact’s source URL carries no recoverable csv, so the receipt cannot be checked against the csv its bytes were fetched under.

  • CSV_MISMATCH – The receipt’s own csv is not the csv its bytes were fetched under, so these bytes belong to a different filing.

  • FILING_TARGET_MISMATCH – The receipt parsed and its csv agrees, but it does not describe this observation’s modelo, ejercicio, period or taxpayer.

UNREADABLE_ARTEFACT
MANIFEST_MISMATCH
UNPARSABLE_PDF
CSV_UNRESOLVABLE
CSV_MISMATCH
FILING_TARGET_MISMATCH
FILED_JUSTIFICANTE_UNREACHED_NOTICE_CODE

Notice code for an artefact that was present but yielded no evidence.

class FiledJustificanteEnrollmentResult(justificante_csvs=(), filing_record_ids=(), conflicting_filing_record_ids=(), notices=(), reconciliation_results=())[source]

Bases: object

Justificante metadata enrolled from filed history and the chain decisions it drove.

Parameters:
justificante_csvs: tuple[str, ...]
filing_record_ids: tuple[str, ...]
conflicting_filing_record_ids: tuple[str, ...]
notices: tuple[Notice, ...]
reconciliation_results: tuple[FilingReconciliationResult, ...]
persist_filed_calculation_observation(observation, *, ports, justificante_csvs=())[source]

Promote one AEAT filed-declaration observation into calculation history.

The persisted row is a registry-grounded cadrumo.domain.calculations.registry.bindings.RegistryModeloObservation stamped with the law-selected registry revision when it can be resolved.

Return type:

str

Parameters:
select_latest_filed_observations_in_history_order(observations)[source]

Return the latest observation per (modelo, year, period) in deterministic history order.

“Latest” is by _filed_observation_rank() (an ALTA registration beats a BAJA, then most-recent presented_at, then expediente_id). History order is _filed_observation_history_period_sort_key() (Modelo 303 IVA fiscal order, quarterly/monthly numeric order elsewhere). This is the single selection-and-ordering authority shared by the calculation-history persistence below and the filed-capture finalizer, so every capture route persists the same observations in the same order and cannot drift.

Return type:

tuple[FiledObservationProtocol, ...]

Parameters:

observations (tuple[FiledObservationProtocol, ...])

enroll_filed_justificante_evidence(observation, *, ports, bucket_id)[source]

Persist matching justificante metadata and reconcile the filing chain with it.

Every parsed receipt that matches the observation becomes one AEAT register entry for reconcile_aeat_register_entry(), carrying the observation’s numeric casillas when all of them are numeric. A chain whose in-force entry belongs to another taxpayer identity is left untouched.

Return type:

FiledJustificanteEnrollmentResult

Returns:

A FiledJustificanteEnrollmentResult of saved CSVs, the chain entries the reconciliations settled on, contradicted pending entries, and every reconciliation result.

Parameters:
persistiva_compensation_history_observations_strict(observations, *, ports)[source]

Persist latest Modelo 303 observations and verify each history row reloads.

Return type:

tuple[str, ...]

Parameters:
latest_declarations_by_period(declarations)[source]

Return the latest period-bearing declaration row per period.

Return type:

tuple[FiledDeclarationProtocol, ...]

Parameters:

declarations (tuple[FiledDeclarationProtocol, ...])

filed_observation_source_metadata(observation, *, justificante_csvs=())[source]

Project one filed observation’s register provenance into persisted metadata.

aeat_tipo_solicitud carries AEAT’s own request-type signal off the register row – the one field that distinguishes an original filing from an amendment. It was previously read into the raw observation and then dropped here, because the source metadata was built from a fixed key set, so the signal existed at capture and was gone by the time anything downstream could read it.

Carrying it is deliberately NOT the same as electing on it: no selection logic reads this key, and which identifier an amendment-aware election should key on stays an open decision. What changes is that the evidence survives, so that decision can be made later against persisted data rather than requiring a re-capture.

The key is omitted rather than written empty when the register row carried no request type. An empty string would be indistinguishable from AEAT declaring one, and absence here means “the row did not say”, which is the honest reading.

The CSV references are written in the shared comparison form, because this is the writing side of a key the cross-period clean-state gate reads back and compares. Deduplicating on a trim alone left two spellings of one identifier surviving as two entries, which is the same second-key defect the comparison side was carrying.

Return type:

dict[str, str]

Parameters:
filed_observation_identity_key(observation)
Return type:

tuple[str, int, str, str]

Parameters:

observation (FiledObservationProtocol)

justificante_csvs_for_observation(observation, justificante_csvs_by_observation)
Return type:

tuple[str, ...]

Parameters: