feat: Contexto Fiscal admin UI - reduces fiscal policy list to 3 real decisions
Adds an ADMIN-only "Contexto Fiscal" section to the Agent detail page: confirm/alter/clear the fiscal decisions
field_contract.resolve_product genuinely cannot derive from the ERP alone (icms_treatment, pis_cofins_profile,
entry_scope). Persists through the existing FiscalContextEntry table (the same one blocker_resolution already
writes operator answers into) - no new persistence model, no opaque JSON box. CSRF + RBAC + audit reuse the exact
mechanism already proven for /agents/{id}/features. Confirming or clearing flags every already-enriched product on
the installation for re-evaluation.
The list was reduced from 5 candidates to 3 by auditing real production data (Agent 2, 2026-09-27/28):
icms_rate/credit_rate and rounding were dropped - both proven NOT_APPLICABLE / ALREADY_DERIVED for this CRT=1
company from real field_contract dry-runs, not assumption. Real evidence surfaced in the UI is a dated, one-time
manual read-only measurement (disclosed in the card copy itself as such) - the Hub has no direct Firebird access,
so this is not a live query; a follow-up should pipe it through the heartbeat the same way CRT/CONTRIBUINTE_IPI
were wired.
Also found, during the same audit, that this client's NFC-e emission ignores the product's cadastral PIS/COFINS
CST/rate fields entirely (verified on the 3 real products that DO have them populated - the emitted documents never
carry the cadastral value) - documented as a strong OPEN recommendation for a future field_contract change, not
applied here, since changing canonical fiscal semantics needs accountant sign-off, not just my own inference.
1129 tests passed (tests + central/tests + agent/tests), 0 failed. Version 1.3.9 (single GTI_FISCAL_VERSION). Hub
only - Agent unchanged, no new installer this release.
This commit is contained in:
@@ -10,6 +10,19 @@
|
||||
{% if ops.enabled %}<form method="post" action="/agents/{{ detail.agent_id }}/pause" style="display:inline"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><button type="submit" class="caution" title="Mantém o heartbeat, mas não executa jobs">Pausar processamento</button></form>{% else %}<form method="post" action="/agents/{{ detail.agent_id }}/resume" style="display:inline"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><button type="submit" class="primary-action" title="Volta a executar jobs">Retomar processamento</button></form>{% endif %}
|
||||
</div></div><dl class="facts"><dt>Processamento</dt><dd><span class="badge {{ '' if ops.enabled else 'critical' }}">{{ "Ativo" if ops.enabled else "Pausado" }}</span> · sincronização automática a cada {{ ops.interval_minutes }} min</dd><dt>Última sincronização</dt><dd>{{ ops.last_sync or "ainda não sincronizado" }}{% if ops.last_changed is not none %} · {{ ops.last_changed }} produto(s) alterado(s){% endif %}</dd><dt>Sincronização agendada</dt><dd>{{ ops.next_sync or ("aguardando o primeiro sync" if ops.enabled else "pausado") }}</dd>{% if ops.rtc %}<dt>Reforma Tributária</dt><dd><span class="badge {{ "" if ops.rtc.enabled else "muted" }}" title="{{ ops.rtc.title }}">{{ ops.rtc.label }}</span></dd>{% endif %}{% if ops.rtc and ops.rtc.enabled %}{% if ops.rtc.products and not ops.rtc.products.applicable %}<dt>cClassTrib</dt><dd>{{ ops.rtc.products.label }}</dd>{% elif ops.rtc.products %}<dt>Produtos RTC</dt><dd><b>{{ ops.rtc.products.headline }}</b><br><small>Total {{ ops.rtc.products.total }} · com cClassTrib válido {{ ops.rtc.products.valid }} · prontos para preenchimento {{ ops.rtc.products.ready }} · ambíguos/bloqueados {{ ops.rtc.products.blocked }}</small></dd>{% endif %}
|
||||
<dt>Preenchimento automático (RTC, não é o Enriquecimento)</dt><dd>{{ ops.rtc.apply.state }}{% if ops.rtc.apply.applied_last_job %} · {{ ops.rtc.apply.applied_last_job }} preenchido(s) no último job{% endif %} · <a href="/agents/{{ detail.agent_id }}/rtc">Controle e candidatos cClassTrib</a></dd>{% endif %}<dt>Último job</dt><dd>{% if ops.last_job %}<a href="/jobs?scope=all&job_id={{ ops.last_job.id }}">{{ ops.last_job.kind_label }}</a> · {{ ops.last_job.status_label }}{% else %}nenhum{% endif %}</dd></dl></section>
|
||||
{% if is_admin %}<section class="panel" id="contexto-fiscal"><div class="panel-title"><div><h2>Contexto Fiscal</h2><p class="muted">Somente as decisões que o motor fiscal (field_contract) realmente não consegue provar sozinho no ERP. Visível apenas para administradores.</p></div></div>
|
||||
{% for d in fiscal_context %}<article class="panel" style="margin-top:12px"><div class="panel-title"><h3>{{ d.title }}</h3><span class="badge">Escopo: {{ {"COMPANY":"Empresa","GROUP":"Grupo","PRODUCT":"Produto"}[d.scope] }}</span></div>
|
||||
<dl class="facts"><dt>Valor atual</dt><dd>{{ d.current_value or "Não confirmado" }}{% if d.last_changed %} <small class="muted">· alterado em {{ d.last_changed }} por {{ d.last_changed_by }}</small>{% endif %}</dd>
|
||||
<dt>Sugestão</dt><dd>{{ d.suggestion or "Sem sugestão automática — evidência insuficiente" }}</dd>
|
||||
<dt>Evidência</dt><dd>{{ d.evidence }} <small class="muted">(medido manualmente em {{ d.measured_at }} — não é uma consulta ao vivo)</small></dd>
|
||||
<dt>Confiança</dt><dd>{{ d.confidence }}</dd>
|
||||
<dt>Campos afetados</dt><dd>{{ d.affected_fields|join(", ") }}</dd></dl>
|
||||
<form method="post" action="/agents/{{ detail.agent_id }}/fiscal-context" class="filters compact-filters"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="key" value="{{ d.key }}">
|
||||
{% if d.allowed_values %}<label>Valor<select name="value">{% for v in d.allowed_values %}<option value="{{ v }}" {% if d.current_value==v %}selected{% endif %}>{{ v }}</option>{% endfor %}</select></label>{% else %}<label>Valor<input name="value" value="{{ d.current_value or '' }}" placeholder="ex.: {01,01,1.65,01,01,7.6} ou INTRA"></label>{% endif %}
|
||||
<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 %}
|
||||
<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