aeat.application.modelo._review_package module

Review-package build and integrity verification for accountant handoff.

A review package is a shareable, checksum-verifiable ZIP archive assembled from an already-verified or filed CalculationRevision: the fichero-BOE draft export bytes, the revision’s typed casilla observations (regulatory grounding included), its bundled ledger filing evidence (when present), and a small package-info descriptor binding everything to the source work unit / bucket / modelo / period.

This module builds and verifies review packages. It reuses the corpus-bundle checksum-manifest primitive (build_corpus_bundle() / verify_corpus_bundle()) rather than re-deriving SHA-256 bundling logic: a review package is, mechanically, a CorpusManifest-checksummed zip whose members happen to be filing artefacts instead of corpus reference files.

Cryptographic signing (ed25519 sender/recipient identity) and the counter-signed accountant feedback-package round trip are explicitly OUT OF SCOPE for this module; verify_review_package() is an INTEGRITY check only (did every member arrive byte-for-byte as built), not an authenticity check (who built it). A future slice adds signing and the counter-sign receipt workflow on top of the manifest this module produces.

The package is a plaintext operator-directed handoff artefact — the operator explicitly requested a shareable export, mirroring the existing export() and export_modelo_revision() plaintext-export exceptions. Nothing here mutates encrypted bucket state or persists a new catalogue record; the ZIP is written directly to the caller-supplied path.

See also

export_modelo_revision():

Produces the fichero-BOE draft bytes bundled into the package.

LedgerFilingEvidence:

The bundled ledger fact basis included when present on the revision.

build_corpus_bundle():

The reused checksum-manifest zip-build primitive.

verify_corpus_bundle():

The reused checksum-manifest zip-verify primitive.

exception ReviewPackageError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: AeatError

Base error for review-package build/verify failures.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
exception ReviewPackageRevisionStateError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: ReviewPackageError

Raised when the source revision is not eligible for a review package.

Eligible states are VERIFICADO_COMPLETO and PRESENTADO (see _ELIGIBLE_REVISION_STATES): a review package shares a filing-grade figure with an accountant, so a bare draft or a superseded revision is refused before any bytes are written.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
exception ReviewPackageIntegrityError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: ReviewPackageError

Raised when a review package fails checksum-manifest verification.

Wraps the underlying CorpusBundleError family (missing/unexpected/mismatched members, manifest tamper, or a structurally invalid archive) behind one review-package-scoped error so callers do not need to import the corpus-manifest error hierarchy.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
class ReviewPackageManifest(**data)[source]

Bases: BaseModel

Package-info descriptor embedded in every review package as JSON.

Distinct from the reused CorpusManifest (which only knows file names, sizes, and digests): this descriptor carries the review-domain identity — which work unit, calculation revision, bucket, modelo, and period the package represents — so a receiving side can address the package without re-parsing the fichero bytes.

Parameters:
  • package_info_version (int)

  • bucket_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=128, pattern=None, ascii_only=None)])

  • work_unit_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=64, max_length=64, pattern=^[0-9a-f]{64}$, ascii_only=None)])

  • calculation_revision_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=64, max_length=64, pattern=^[0-9a-f]{64}$, ascii_only=None)])

  • modelo (str)

  • filing_year (int)

  • period (Period)

  • revision_state (str)

  • has_ledger_evidence (bool)

  • built_at (datetime)

  • built_by (str)

  • notes (str)

package_info_version: int
bucket_id: BucketId
work_unit_id: WorkUnitId
calculation_revision_id: CalculationRevisionId
modelo: str
filing_year: int
period: Period
revision_state: str
has_ledger_evidence: bool
built_at: datetime
built_by: str
notes: str
class ReviewPackageBuildResult(**data)[source]

Bases: BaseModel

Receipt produced by build_review_package().

Parameters:
output_path: Path
manifest: ReviewPackageManifest
corpus_root_name: str
member_count: int
class ReviewPackageVerification(**data)[source]

Bases: BaseModel

Outcome of verify_review_package().

missing / unexpected / mismatched mirror CorpusBundleVerification exactly (the review package’s checksum layer IS a corpus bundle); manifest is the review-specific ReviewPackageManifest recovered from the package’s package-info.json member once the archive is confirmed clean enough to read it.

Parameters:
manifest: ReviewPackageManifest
missing: tuple[str, ...]
unexpected: tuple[str, ...]
mismatched: tuple[str, ...]
property is_clean: bool

Return True iff every archived member matches its checksum record.

build_review_package(*, revision, work_unit, draft_bytes, output_path, built_by, notes='', generated_at=None)[source]

Assemble a shareable, checksum-verifiable review package ZIP.

revision must be in a filing-grade state (VERIFICADO_COMPLETO or PRESENTADO); see _ELIGIBLE_REVISION_STATES. work_unit is the revision’s parent (carries bucket_id / modelo / filing_year / period, which CalculationRevision itself does not store); the caller must confirm work_unit.work_unit_id == revision.work_unit_id before calling — this function trusts that binding rather than re-resolving it, keeping this module free of a catalogue-repository dependency. draft_bytes is the already-rendered fichero-BOE artefact (the caller obtains this from export_modelo_revision(), which owns every export-time safety gate — evidence completeness, cross-period clean state, IVA wallet reconciliation — so this function does not re-validate export eligibility beyond the revision-state check above).

The package bundles four members under a checksum manifest built by build_corpus_bundle() (reused verbatim, not re-derived):

  • draft.fichero-boe — the rendered filing artefact bytes.

  • revision.json — the full CalculationRevision, including its typed observations (legal_refs / source_refs grounding for every casilla).

  • evidence.json — the revision’s bundled LedgerFilingEvidence when present, or an explicit {"present": false} marker when the revision carries no ledger evidence (a non-ledger modelo, or a manual-only filing).

  • package-info.json — the ReviewPackageManifest descriptor.

Members are written to a temporary staging directory then packed with build_corpus_bundle(), which itself writes atomically (temp-file-then-rename) so a build failure never leaves a partial package at output_path.

Raises:
Return type:

ReviewPackageBuildResult

Parameters:
verify_review_package(package_path)[source]

Verify a review package’s checksum manifest and recover its descriptor.

Delegates the checksum layer entirely to verify_corpus_bundle() (no hashing logic is re-derived here). When the archive is clean, the embedded package-info.json member is loaded and validated into a ReviewPackageManifest. A dirty archive still returns a best-effort manifest recovery when package-info.json itself matches its checksum record (so the caller can report WHICH package failed even when some other member is missing or mismatched); if the descriptor itself cannot be recovered, verification fails loudly rather than fabricating a manifest.

This is an INTEGRITY check only: it confirms every archived member matches the manifest byte-for-byte. It makes no claim about WHO built the package (that is the deferred cryptographic-signing slice).

Raises:
  • FileNotFoundError – If package_path does not exist.

  • ReviewPackageIntegrityError – If the archive is not a valid zip, the embedded corpus manifest is absent/structurally invalid/tampered, or the package-info.json member cannot be recovered.

Return type:

ReviewPackageVerification

Parameters:

package_path (Path)

assert_review_package_verifies(package_path)[source]

Verify package_path and raise on any drift; return its descriptor on success.

Mirrors assert_corpus_bundle_verifies() for the review-package surface: the operator-facing assertion a receiving side calls before trusting a handed-over package.

Return type:

ReviewPackageManifest

Parameters:

package_path (Path)