aeat.domain.calculations.registry._groi_oracle module

AEAT GROI Spanish-ROI consult oracle.

The GROI servlet at www2.agenciatributaria.gob.es certifies whether a given Spanish NIF is registered in the AEAT Registro de Operadores Intracomunitarios. Live probing on 2026-05-07 confirmed the surface is reachable under cl@ve-movil authentication; the live driver lives at aeat.adapters.outbound.aeat.sede._groi_check and this module wraps it as a LiveParityOracle.

The oracle is the SPANISH-counterparty sibling of _aeat_nif_iva_oracle (foreign-EU VIES proxy). Both share the iva_id_check surface kind and pair with cross-references whose surface is public_read_surface; the registry’s surface-kind compatibility table at _live_parity._COMPATIBLE_SURFACE_PAIRS already declares that pair.

READ-ONLY MANDATE

AEAT writes are PERMANENTLY FORBIDDEN. The GROI form’s submit handler is an HTTP POST to ConsultaOperadorSedeGroiServlet — but per AEAT service contract this POST is a CONSULT (a SELECT against the ROI registry) and modifies no AEAT-side state. The submitting NIF is not recorded against the queried NIF, no draft is created, no filing history entry is generated. Every observation captured during live probing 2026-05-07 confirmed the form’s only side effect is rendering a verdict page back to the caller.

Defense-in-depth nonetheless: the registry’s RemoteStateGuard fence intercepts every operation the oracle emits BEFORE any browser action runs. Any guard policy attached to a GROI cross-reference MUST declare forbidden_actions containing the canonical AEAT_WRITE_FORBIDDEN_ACTIONS set so that, were a future driver refactor to mislabel an operation (or were AEAT to silently change the endpoint to a state-modifying action), the guard rejects the operation BEFORE it leaves the process. The unit and live tests exercise the guard with deliberately fabricated write operations to prove the read-only invariant by construction.

class GroiObservation(**data)[source]

Bases: _CheckerBaseModel

Observed Spanish-ROI verdicts returned by an executable adapter.

values keys are upper-cased Spanish NIFs; values values are lowercase verdict tokens (valid / invalid / unknown).

Parameters:
values: dict[str, str]
raw_evidence_locator: str | None
class GroiDriver(*args, **kwargs)[source]

Bases: Protocol

Execution boundary for GROI live or replay adapters.

property mode: Literal['live', 'replay']

Discriminate which execution surface this driver speaks to.

Returns either "live" (a driver that drives the real AEAT GROI servlet through an authenticated browser session) or "replay" (a driver that decodes a previously captured response from local corpus bytes). GroiOracle reads this to label evidence provenance and to decide whether a run touched the network.

Returns:

The literal "live" or "replay".

planned_operations(payload, *, expected)[source]

Declare the remote operations this driver intends to perform.

The operations are pre-flighted through the registry’s remote-state guard before any of them runs, so the read-only invariant can be enforced by construction. The driver returns the sequence it would execute for the given probe rather than executing it.

Parameters:
  • payload (bytes) – Raw adapter input bytes (a live request envelope or a captured replay document, depending on the driver mode).

  • expected (Mapping[str, object]) – Mapping of Spanish NIF (the per-taxpayer fiscal identifier) to its expected ROI-registration verdict; ROI is the AEAT Registro de Operadores Intracomunitarios, the register of operators cleared for intra-EU trade.

Return type:

tuple[RemoteOperation, ...]

Returns:

The ordered RemoteOperation tuple this driver would emit.

collect_observation(payload, *, expected)[source]

Execute the GROI probe and return the observed verdicts.

Parameters:
  • payload (bytes) – Raw adapter input bytes for this driver mode.

  • expected (Mapping[str, object]) – Mapping of Spanish NIF to its expected ROI-registration verdict, used by live drivers to scope which identifiers to query.

Return type:

GroiObservation

Returns:

A GroiObservation whose values map upper-cased NIFs to lowercase verdict tokens (valid / invalid / unknown).

class GroiReplayDriver[source]

Bases: object

Deterministic local replay driver for captured GROI outputs.

Payload shape:

{
  "observed": {"A28015865": "valid", "B12345678": "invalid"},
  "raw_evidence_locator": "corpus/aeat_official/groi_response_samples/..."
}
property mode: Literal['replay']

Identify this driver as a deterministic local replay.

Always "replay": this driver decodes a captured GROI response from local corpus bytes and never touches the network, making it the offline counterpart used by the parity test suite.

Returns:

The literal "replay".

planned_operations(payload, *, expected)[source]

Declare the single local-parse operation a replay performs.

Both arguments are ignored: a replay reads a fixed captured document and performs no network or browser action, so it returns exactly one RemoteOperation of kind local_workbook with action parse-groi-replay. The remote-state guard still pre-flights this list for uniformity with the live path.

Parameters:
  • payload (bytes) – Captured replay bytes; ignored.

  • expected (Mapping[str, object]) – Expected NIF-to-verdict mapping; ignored.

Return type:

tuple[RemoteOperation, ...]

Returns:

A one-element tuple naming the local replay-parse RemoteOperation.

collect_observation(payload, *, expected)[source]

Decode the captured replay payload into observed verdicts.

The expected mapping is ignored; the observation is read straight from the captured document. The payload is a JSON envelope with an observed object (NIF to verdict) and an optional raw_evidence_locator pointing at the corpus sample on disk.

Parameters:
  • payload (bytes) – JSON replay bytes; decoded via the shared replay decoder.

  • expected (Mapping[str, object]) – Expected NIF-to-verdict mapping; ignored.

Return type:

GroiObservation

Returns:

A GroiObservation carrying the captured verdicts and evidence locator.

Raises:

RegistryValidationError – If the payload is malformed or carries blank NIF keys or verdict values.

class GroiOracle(*, driver=None)[source]

Bases: BaseCheckerOracle[GroiObservation]

AEAT-mediated Spanish-ROI registration validator.

Wraps a GroiDriver (live or replay). When no driver is configured the oracle still pre-flights the planned operations through the remote-state guard; verify_payload then returns unverifiable because no observation was available for comparison. With a driver configured the oracle compares the expected verdict per NIF against the driver-emitted observation and returns match / mismatch accordingly.

Parameters:

driver (GroiDriver | None)

surface_label: str
property oracle_id: OracleId

Return the stable catalogue identifier for this oracle.

The value is the module-level GROI_ORACLE_ID constant (aeat-groi-spanish-roi-checker), used as the key under which the oracle registers in the live-parity catalogue.

Returns:

The catalogue registration key.

property surface_kind: Literal['file_validator', 'open_simulator', 'iva_id_check', 'pre_filing_validator', 'integration_test_service']

Return the surface-kind tag that gates cross-reference pairing.

Always "iva_id_check": the same tag carried by the VIES sibling oracle. VIES (VAT Information Exchange System) is the EU service that checks foreign-EU VAT identifiers, the GROI equivalent for the rest of the EU. Sharing the tag lets the registry’s surface-kind compatibility table pair GROI checks against public_read_surface cross-references (other read-only public lookups).

Returns:

The OracleSurfaceKind literal "iva_id_check".

planned_operations(payload, *, expected)[source]

Declare the remote operations a GROI verification would perform.

The operations are pre-flighted through the remote-state guard before any browser action runs, enforcing the read-only AEAT mandate by construction. With a driver configured the call delegates to that driver. With no driver configured the oracle synthesises the canonical live sequence directly: a GET against the GROI servlet URL pulled from central config, a browser action to open the form, one check-nif-<nif> action per expected NIF in sorted order, and a final discard-session action.

Parameters:
  • payload (bytes) – Raw adapter input bytes, forwarded to a configured driver.

  • expected (Mapping[str, object]) – Non-empty mapping of Spanish NIF to its expected ROI-registration verdict; NIF is the per-taxpayer fiscal identifier and ROI is the AEAT register of intra-EU operators.

Return type:

tuple[RemoteOperation, ...]

Returns:

The ordered RemoteOperation tuple for this verification.

Raises:

RegistryValidationError – If expected is empty, or if any NIF key or verdict value is blank.

register_default(catalogue, *, environment=OracleEnvironment.PRODUCTION)[source]

Register the GROI Spanish-ROI oracle under the requested environment.

Return type:

None

Parameters: