cadrumo.adapters.outbound.aeat.sede.walker module

Playwright-driven sede walker: session → expedientes → PDF bytes.

The walker is the only side-effectful layer in adapters.outbound.aeat.sede. It takes an AeatSession whose encrypted browser state carries valid AEAT cookies, drives a read-only Playwright session over the sede, and exposes two operations to callers:

Both are read-only by construction: only page.goto and context.request.get cross the wire. No click() onto submit buttons, no form POSTs, no mutation verbs anywhere in the public surface.

assert_resumen_landing(landing_url)[source]

Refuse a landing that is not the expedientes resumen page.

Called after the branch expansion, which JS-clicks every mostrarListado anchor in the tree. Those anchors are AJAX expanders, so the page is expected not to move – but nothing established that it had not. The walker then snapshots the DOM and parses it AS the expedientes tree, so a navigation during expansion would be read as a tree with no error anywhere: the parser would simply find no rows, and an empty result is indistinguishable from a taxpayer with no expedientes.

Parameters:

landing_url (str) – The URL AEAT actually served, read off the page.

Raises:

SedeNavigationError – When the walker is not on the resumen page.

Return type:

None

assert_landed_url_readable(landed_url, *, requested_url)[source]

Return the landed URL, refusing a navigation that produced no readable one.

The re-assertion after a goto used to be skipped entirely when the landed URL was empty, so the one case where the outcome could not be established was the one case that was not checked – fail-open in exactly the position the re-assertion exists to cover.

Parameters:
  • landed_url (str) – page.url after the navigation.

  • requested_url (str) – The URL the navigation asked for, for diagnostics.

Return type:

str

Returns:

The landed URL, once established as readable.

Raises:

SedeNavigationError – When the navigation produced no readable URL.

async walk_expedientes_tree(session, *, modelo=None, settings=None)[source]

Enumerate every expediente visible under Mis Expedientes.

The sede renders an AJAX-expanded tree. This function expands every category branch whose label contains a Modelo <N> token (or all branches when modelo is None), then parses the resulting DOM for leaf expediente rows.

Parameters:
  • session (AeatSession) – An authenticated session with encrypted cached AEAT cookies. Cl@ve-móvil and certificate sessions both qualify.

  • modelo (str | None) – When set, only expand category branches whose label references this modelo code (e.g. "100" for IRPF). Saves DOM expansion work on large corpora.

  • settings (Settings | None) – Optional Settings override. Defaults to core.config.load_settings().

Return type:

tuple[Expediente, ...]

Returns:

Tuple of Expediente records, ordered as AEAT renders them (most recent first in every captured case so far).

Raises:

SedeNavigationError – If goto or a required expansion fails.

async capture_justificante(session, expediente, *, settings=None)[source]

End-to-end: expediente → CSV handle → PDF bytes → SedeCapture.

Navigates to the expediente detail page, extracts its CSV reference, and fetches the raw PDF in one session-reusing call. This is the preferred entry point for callers that just want “the AEAT record for this expediente”.

Parameters:
Return type:

SedeCapture

Returns:

A fully populated SedeCapture.

Raises: