cadrumo.adapters.outbound.aeat.sede.groi_check module¶
AEAT GROI Spanish-ROI consult browser driver.
Drives the public AEAT-hosted GROI servlet that confirms whether a
given Spanish NIF is registered as an intra-community operator
(Registro de Operadores Intracomunitarios). The form accepts a
single 9-character Spanish NIF, posts to the same servlet path, and
renders a verdict text such as CONSTA UN OPERADOR INTRACOMUNITARIO
(registered) or no es un NIF válido (input-format error).
This adapter is the SPANISH-counterparty sibling of the IXVI
_nif_iva_check adapter (foreign-EU VIES proxy). Live probing on
2026-05-07 confirmed:
The GROI servlet at www2 is reachable under cl@ve-movil auth.
The IXVI servlet at www1 requires a stricter auth tier than cl@ve-movil and is currently blocked.
The two surfaces serve different verification needs:
GROI (this adapter): “is this Spanish NIF registered as an intra-community operator in Spain?” — used to confirm Spanish counterparties on modelo 349 are ROI-registered.
IXVI (
_nif_iva_check): “is this foreign EU IVA-ID valid per VIES?” — used to confirm foreign EU counterparties on modelo 349 hold a valid IVA identifier.
The driver reaches the form servlet directly via authenticated
BrowserSession (caller is responsible for loading the cl@ve-movil /
certificate storage state). Form selectors are verified against
captured live HTML (form action ConsultaOperadorSedeGroiServlet;
input id=nif name=nif maxlength=9; submit id=enviar
name=enviar); verdict markers are derived from real AEAT response
samples captured 2026-05-07.
- assert_groi_read_landing(landing_url)[source]¶
Refuse a landing that is not the GROI consult servlet.
The pre-submit form-action check confirms where a click WILL post; this confirms where AEAT actually SERVED. They are different questions: the form action is read off a DOM AEAT controls, and a redirect chain after the POST is invisible to it. Called after every navigation and after every submit, so the verdict parser only ever reads a page this driver is entitled to be on.
Public so the driver’s no-write proof exercises this exact rule rather than a mirrored copy, which would keep agreeing with itself after the rule changed shape.
- Parameters:
landing_url (
str) – The URL AEAT actually served, read off the page.- Raises:
SedeNavigationError – When the landing is not the GROI servlet.
- Return type:
- class GroiNifVerdict(**data)[source]¶
Bases:
_SedeCheckerModelOne observation per declared Spanish NIF after live navigation.
verdictis the AEAT-rendered ROI-registration status:validwhen AEAT reports the NIF as a registered intra-community operator,invalidwhen AEAT either says the NIF is unregistered or that the input format is malformed,unknownwhen the response text is structurally unanswerable.- Parameters:
data (Any)
- nif: str¶
- verdict: IdentityCheckVerdictValue¶
- raw_evidence_locator: str | None¶
- class GroiResult(**data)[source]¶
Bases:
_SedeCheckerModelAggregate live-driver result across every declared Spanish NIF.
- Parameters:
data (Any)
- observations: tuple[GroiNifVerdict, ...]¶
- class GroiSedeDriver(*, settings=None)[source]¶
Bases:
objectLive AEAT GROI driver backed by the central BrowserSession surface.
The driver navigates directly to the form servlet (the surface is reachable post cl@ve-movil auth without an intermediate sede entry page) and queries each declared NIF in alphabetical order. Verdict parsing keys off the AEAT certification phrases captured live.
- Parameters:
settings (Settings | None)
- property mode: Literal[CheckerDriverMode.LIVE]¶
Always
"live"— the driver requires a real Playwright session.
- planned_operations(payload, *, expected)[source]¶
Return the ordered list of
RemoteOperationentries planned for this driver run.Builds the sequence: GET the GROI URL, open the form, then one
check-nif-<NIF>browser action per declared NIF (sorted alphabetically), and finallydiscard-session. At least one entry inexpectedis required; raisesRegistryValidationErrorotherwise.
- collect(payload, *, expected, timeout_ms=30000)[source]¶
Run the async GROI driver synchronously and return a
GroiResult.Wraps
collect_async()inasyncio.run.SedeError,SiteHealthError, andBrowserErrorare re-raised asRegistryValidationErrorfor the registry oracle layer.
- async collect_async(payload, *, expected, timeout_ms=30000)[source]¶
Async entry point returning
GroiResult— delegates tocollect_groi_observations().
- collect_observation(payload, *, expected)[source]¶
Return the canonical checker observation from the per-NIF result.
Drives the live GROI form via
collect(), then collapses the per-NIF observations into a flat{nif: verdict}mapping that the GROI oracle wrapper compares against the caller’s expected verdicts.- Return type:
- Parameters:
- async collect_groi_observations(payload, *, expected, settings=None, timeout_ms=30000, browser_session_factory=None)[source]¶
Drive the GROI form per declared NIF and return a
GroiResultwith one observation each.