3.8 KiB
Agent company context rules V1
Goal: identify ACTIVE_COMPANY, COMPANY_UF and COMPANY_CRT for each Agent installation deterministically and auditably. Read-only; no fiscal rule is created.
What the evidence proved (4 SGV databases, docs/COMPANY_CONTEXT_RESOLUTION_V1.json)
- Company identity:
EMISSOR.EMITENTE_CNPJequals exactly oneC000004.CNPJin every database, and 100% of the emitted NFC-e access keys (model 65, cUF 29) carry that CNPJ. In RINCAO the other two C000004 rows (SP agro company, refrigeration company) have no emitted documents. - Emission authority for CRT/UF/CNPJ =
EMISSOR: the emission programs (GtiVendas_NFCe.exe,Envio NF-e.exe) bindQrEmissor.EMITENTE_CRT/EMITENTE_UF/EMITENTE_CNPJand never referenceC000004.CRTorOPTANTE_SIMPLES. The ERP (SGV) bindsC000004.CRT(qrFilialCRT,QrSPEDFilialCRT) andOPTANTE_SIMPLESfor the filial registry/SPED. The CRT tag inside issued XML could not be read:C000061.NFE_XMLandXML_MES.XMLstore file paths, not content. - UF: EMISSOR.EMITENTE_UF, C000004.UF and the cUF of emitted keys agree everywhere (
UF_SOURCE_CONFIRMED). - CRT: agrees in CONSTRUCAO only. MMMODAS (EMISSOR 1 / C000004 2), RINCAO (3 / 1), UNIAO (1 / 3) are
CRT_SOURCE_CONFLICT. OPTANTE_SIMPLES also disagrees with CRT in MMMODAS and UNIAO.
Resolution algorithm (agent/src/gti_fiscal_agent/company_context.py)
- Company. Configured
COMPANY_CODE/COMPANY_CNPJmust identify exactly one C000004 row. Otherwise: one C000004 row → it; several rows → the single row whose CNPJ equals the single EMISSOR CNPJ; anything else →CONFIG_REQUIRED. First row / lowest code / latest row are never used. - Emitter cross-check. EMISSOR must describe the selected company, else
CONFLICT(also covers a configured company that is not the emitter). - Documents. If NFC-e keys exist, some must carry the selected CNPJ (none →
CONFLICT); other CNPJs are only a warning (HISTORICAL_NFCE_KEYS_FROM_OTHER_CNPJ, e.g. the test database). - UF. EMISSOR, C000004 and document-key sources must agree; disagreement →
CONFLICT, none →UNRESOLVED. - CRT. EMISSOR and C000004 agree →
CRT_SOURCE_CONFIRMED. They disagree →CONFLICTunlessCOMPANY_CRT_SOURCE=EMISSOR|C000004is configured (resolved,CONFIGURED, confidence MEDIUM, warning recorded). Single source → accepted with warning. None/invalid →UNRESOLVED. Recommendation from the evidence:EMISSOR. OPTANTE_SIMPLESincoherent with the CRT is reported as a warning, never used to decide.
Preflight
python agent/scripts/preflight_company_context.py [--database <FDB>] prints COMPANY_CONTEXT=PASS|FAIL, company_code, masked CNPJ (first/last 2 digits), UF, CRT, source_of_truth, confidence, warnings, reasons; exit code 2 on FAIL. run_agent_service.py runs it at startup and aborts on FAIL. read_company_context() raises CompanyContextError on FAIL. It fails on: several companies without selection, configured CNPJ/code not found, CRT or UF undeterminable, emitter/selection divergence, unresolved CRT conflict.
Results without configuration
| database | class | reason |
|---|---|---|
| CONSTRUCAO | AUTO_RESOLVED | single row, all sources agree |
| MMMODAS | CONFLICT | CRT EMISSOR=1 vs C000004=2 |
| RINCAO | CONFLICT | company resolved through EMISSOR CNPJ; CRT EMISSOR=3 vs C000004(row)=1 |
| UNIAO | CONFLICT | CRT EMISSOR=1 vs C000004=3 |
With COMPANY_CRT_SOURCE=EMISSOR the three resolve (CONFIGURED); a configured non-emitting company or an unknown CNPJ still fails (tested on RINCAO). |
Minimal generic Agent requirement
C000004 + C000025; supports_ncm, supports_ipi, supports_cest; a resolvable company context (this preflight PASS); the executor's live column type/length guard. Everything else is capability-based (scripts/sgv_capabilities.py); schema fingerprint equality is NOT required.