feat: Hub-side plumbing for CFOP/IPPT write capability (flag DEFAULT OFF)
Missão 4 / Bloco E.1-E.2, on feature/cfop-ippt-write only (never main):
- Migration 0033_cfop_ippt_write_mode: Installation.cfop_ippt_write_mode
(DISABLED|DRY_RUN|APPROVAL_REQUIRED|ENABLED, DEFAULT 'DISABLED' for every
installation, existing and new) + append-only cfop_ippt_write_audits table.
- central/app/cfop_ippt_audit.py mirrors rtc_audit.py for this narrower
capability: job_authorizations(installation) (delivered inside the
authenticated job/heartbeat authorizations, never a local Agent switch) and
record_cfop_ippt_write_audits (persists applied/aborted cells; only CFOP/
IPPT are ever accepted, everything else silently ignored).
- central/app/web.py: POST /agents/{id}/cfop-ippt-write - ADMIN only (SSO
Gerenciador), CSRF, typing GRAVAR required for any mode other than
DISABLED, every change audited (CFOP_IPPT_WRITE_MODE). Small toggle UI in
agent_detail.html next to Contexto Fiscal.
- Wired into the existing job/heartbeat authorization payload and job
completion handling in central/app/api.py, alongside (never mixed with)
the RTC wiring.
- 18 new tests (central/tests/test_cfop_ippt_write_plumbing.py): default
DISABLED everywhere (job payload AND /v1/agents/me/authorizations); a
forged completion payload can never change the mode (read fresh from the
Installation row, never from Agent-supplied JSON); PADRAO role gets 403
with no audit row; ADMIN + wrong/missing CSRF is refused; ADMIN without
typing GRAVAR is refused (422); disabling never requires GRAVAR; invalid
mode is refused; the audit recorder only accepts CFOP/IPPT, silently
drops any other field (e.g. a stray CCLASSTRIB row).
- Two existing RTC tests updated for the additive authorizations-dict key
(cfop_ippt_write_mode) they now also carry.
Still NOT wired into agent/scripts/run_agent_service.py::execute_job - no
Agent code calls select_candidates/apply_candidates yet, so nothing writes
to Firebird from this branch. That wiring, an Agent version bump, a new
installer and Windows Sandbox validation are the next step, deliberately
left for after review (see docs/CFOP_IPPT_WRITE_CAPABILITY_V1.md) - this
mission explicitly asked not to publish this as an active release yet.
FULL_SUITE on this branch: 1 known, expected failure -
test_manifest_records_the_real_migration_head - because this branch adds
migration 0033 that was never merged to master/deployed; RELEASE_MANIFEST.json
intentionally still describes master/production (head 0032). Every other
test passes, including all CFOP/IPPT and RTC-adjacent tests.
REAL_PRODUCT_FISCAL_WRITES=0 (no write path is reachable from any Agent
code on any branch).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,13 @@
|
||||
<label>Motivo<input name="reason" placeholder="opcional" autocomplete="off"></label>
|
||||
<button class="secondary" type="submit" name="fc_action" value="confirm">Confirmar</button>
|
||||
<button class="secondary" type="submit" name="fc_action" value="clear" onclick="return confirm('Limpar esta decisão? Os produtos voltam a depender só do que o ERP prova.')">Limpar</button></form>
|
||||
</article>{% endfor %}</section>{% endif %}
|
||||
</article>{% endfor %}
|
||||
{% if is_admin %}<article class="panel" style="margin-top:12px"><div class="panel-title"><h3>Escrita CFOP/IPPT (Missão 4)</h3><span class="badge {{ '' if cfop_ippt_write_mode == 'DISABLED' else 'critical' }}">{{ cfop_ippt_write_mode }}</span></div>
|
||||
<p class="muted">Capability CFOP_IPPT_WRITE_V1: escreve apenas C000025.CFOP e C000025.IPPT, só as células que o motor já classificou AUTO_FILL_SAFE. Desativada por padrão em toda instalação nova.</p>
|
||||
<form method="post" action="/agents/{{ detail.agent_id }}/cfop-ippt-write" class="filters compact-filters"><input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<label>Modo<select name="mode"><option value="DISABLED" {% if cfop_ippt_write_mode=='DISABLED' %}selected{% endif %}>Desativado</option><option value="DRY_RUN" {% if cfop_ippt_write_mode=='DRY_RUN' %}selected{% endif %}>Simulação (não grava)</option><option value="APPROVAL_REQUIRED" {% if cfop_ippt_write_mode=='APPROVAL_REQUIRED' %}selected{% endif %}>Requer aprovação</option><option value="ENABLED" {% if cfop_ippt_write_mode=='ENABLED' %}selected{% endif %}>Ativo</option></select></label>
|
||||
<label>Para ativar (qualquer modo além de Desativado) digite GRAVAR<input name="confirm" autocomplete="off"></label>
|
||||
<button class="secondary" type="submit">Salvar</button></form></article>{% endif %}</section>{% endif %}
|
||||
<section class="panel" id="funcionalidades"><div class="panel-title"><div><h2>Funcionalidades (Enriquecimento de produtos novos)</h2><p class="muted">Decididas no Hub para esta instalação — o Agent não tem chave local. O Enriquecimento de produtos novos NÃO é o preenchimento RTC, que tem controle próprio (<a href="/agents/{{ detail.agent_id }}/rtc">controle e candidatos</a>).</p></div></div>
|
||||
{% if features.waiting %}<p class="coverage-status error">Aguardando configuração de funcionalidades: esta instalação ainda não processa nada.</p>{% endif %}
|
||||
<dl class="facts"><dt>Corretor fiscal</dt><dd><span class="badge {{ '' if features.corrector_enabled else 'critical' }}">{{ "Ativo" if features.corrector_enabled else "Desativado" }}</span></dd><dt>Enriquecimento de produtos novos</dt><dd><span class="badge {{ '' if features.enrichment_mode != 'DISABLED' else 'critical' }}">{{ features.state_label }}</span></dd></dl>
|
||||
|
||||
Reference in New Issue
Block a user