Pular para conteúdo

Operações Fiscais e CFOPs

Objetivo

Documentar o funcionamento das Operações Fiscais, a tabela de CFOPs e o motor de determinação automática que seleciona a regra tributária correta para cada transação. A operação fiscal é o coração da parametrização tributária brasileira: ela define qual CFOP, CST, alíquota e imposto serão aplicados em cada pedido de venda, compra ou fatura, baseando-se em uma combinação rica de critérios como tipo de produto, cliente, UF e NCM.


Implementação Técnica

  • Model: l10n_br_ciel_it_account.operacao — classe L10nBrOperacao
  • Arquivo base (Saída): l10n_br_ciel_it_account/models/sale_order.py (a partir da linha 5542)
  • Herança (Entrada): l10n_br_ciel_it_account/models/purchase.py (linha 3063) — adiciona l10n_br_tipo_pedido_entrada
  • Herança (conta): Não há herança adicional em account.py — usa o mesmo model
  • Menu do Sistema: Vendas → Configuração → Fiscal → Operação

Models auxiliares definidos no mesmo escopo

Model Classe Descrição
l10n_br_ciel_it_account.mensagem.fiscal L10nBrMensagemFiscal Mensagens fiscais padrão para NF
l10n_br_ciel_it_account.cfop L10nBrCfop Tabela de CFOPs (entradas e saídas)
l10n_br_ciel_it_account.tipo.documento L10nBrTipoDocumento Tipos de documento fiscal (NF-e 55, NFS-e, etc.)
l10n_br_ciel_it_account.codigo.servico L10nBrCodigoServico Códigos de serviço (LC 116/03)
l10n_br_ciel_it_account.classificacao.tributaria.ibscbs Classificação tributária IBS/CBS
l10n_br_ciel_it_account.classificacao.tributaria.is Classificação tributária IS

CFOP (l10n_br_ciel_it_account.cfop)

Definição

O Código Fiscal de Operações e Prestações (CFOP) classifica a natureza da operação fiscal e é obrigatório em todos os documentos fiscais brasileiros.

Campos

Campo Tipo Descrição
name Char Descrição do CFOP
codigo_cfop Char (unique) Código numérico (ex: 5102, 6102, 7102)
l10n_br_destino_operacao Selection Destino: Municipal, Estadual, Interestadual, Exterior
l10n_br_ipi_cst Selection CST padrão do IPI para este CFOP
l10n_br_pis_cst Selection CST padrão do PIS
l10n_br_cofins_cst Selection CST padrão de COFINS
l10n_br_ind_nfe Boolean Indica se é CFOP de NF-e
l10n_br_ind_comunicacao Boolean Indica se é CFOP de comunicação
l10n_br_ind_transporte Boolean Indica se é CFOP de transporte
l10n_br_ind_devolucao Boolean Indica se é CFOP de devolução

Convenção de numeração

Prefixo Escopo
1.xxx Entradas — mesma UF
2.xxx Entradas — outra UF
3.xxx Entradas — exterior
5.xxx Saídas — mesma UF
6.xxx Saídas — outra UF
7.xxx Saídas — exterior

Operação Fiscal (l10n_br_ciel_it_account.operacao)

Definição

A Operação Fiscal é o modelo mais complexo e poderoso da localização. Ela funciona como uma regra de determinação: para uma combinação específica de contexto (produto, cliente, UF, regime tributário, etc.), define qual CFOP e quais parâmetros de impostos devem ser aplicados.

O sistema busca entre todas as operações cadastradas a que melhor se encaixa na situação atual. Se uma operação tem filtros específicos (ex: NCM, parceiro), ela tem prioridade sobre operações genéricas.

Constraint

_sql_constraints = [
    ('name_uniq', 'unique(name)', 'O Nome deve ser unico !')
]

Seção 1 — Campos de Filtro (Critérios de Matching)

Esses campos definem quando a operação se aplica. O motor de determinação monta um domain dinâmico que busca operações onde cada campo CONFERE com o contexto atual OU está vazio (wildcard).

Critérios Obrigatórios (filtros exatos)

# Campo Tipo Valores Lógica no motor
1 l10n_br_tipo_operacao Selection saida, entrada Match exato — filtra por tipo (vendas = saída, compras = entrada)
2 l10n_br_tipo_pedido Selection(Saída) venda, servico, exportacao, bonificacao, ... (49 valores) Match exatotipo_pedido do pedido de venda
3 l10n_br_tipo_pedido_entrada Selection(Entrada) compra, importacao, devolucao, ... (38 valores) Adicionado por herança no purchase.py
4 company_id Many2one Empresa do contexto Match exato — sempre filtra pela empresa

Critérios de Contexto (match ou wildcard)

Para cada um desses campos, o motor usa a lógica: campo da operação == valor do contexto OU campo da operação está vazio.

# Campo Tipo De onde vem o valor no contexto Derivação automática
5 l10n_br_tipo_produto Selection Derivado do produto servico se serviço, consumivel se consumível, comprado se purchase_ok, senão produzido
6 l10n_br_tipo_cliente Selection Derivado do parceiro pf se CPF, ex se país diferente, zf se IS preenchido, senão pj
7 l10n_br_orgao_publico Selection partner.l10n_br_orgao_publico Direto do cadastro do parceiro
8 l10n_br_regime_tributario Selection partner.l10n_br_regime_tributario Direto do cadastro do parceiro
9 l10n_br_indicador_ie Selection partner.l10n_br_indicador_ie 1=Contribuinte, 2=Isento, 9=Não contribuinte
10 l10n_br_origem Selection line.l10n_br_origem ou product.l10n_br_origem Origem do produto (0-8 conforme tabela ICMS)
11 l10n_br_destino_operacao Selection Derivado da UF 1=interna (mesma UF), 2=interestadual, 3=exterior
12 l10n_br_tipo_destinacao Selection line.l10n_br_compra_indcom uso, ind, com, ativo, etc. Fallback: uso
13 l10n_br_operacao_icmsst Selection Derivado do NCM/UF ncmuf se existe tabela NCM/UF com ICMS ST, senão vazio

Critérios Many2many (filtros opcionais de especificidade)

Se o campo está preenchido na operação, o item deve estar na lista. Se está vazio, aceita qualquer valor (wildcard).

# Campo Tipo Match contra *_is_set computado
14 categ_ids M2M product.categ_id categ_is_set
15 ncm_ids M2M product.l10n_br_ncm_id ncm_is_set
16 servico_ids M2M product.l10n_br_codigo_servico (por nome) servico_is_set
17 product_ids M2M product.id product_is_set
18 fiscal_tag_ids M2M product.fiscal_tag_ids fiscal_tag_is_set
19 partner_ids M2M partner.id partner_is_set
20 partner_fiscal_tag_ids M2M partner.fiscal_tag_ids partner_fiscal_tag_is_set
21 partner_state_ids M2M partner.state_id partner_state_is_set
22 l10n_br_municipio_ids M2M Município ISS ou do parceiro l10n_br_municipio_is_set

Critérios Adicionais

# Campo Tipo Lógica
23 l10n_br_valor_minimo Float Operação só se aplica se o total da NF supera este valor. Match: valor_minimo <= total_nfe OR valor_minimo == 0
24 l10n_br_cfop_orig_id Many2one CFOP de origem do DF-e. Usado em entradas para match com o CFOP do documento do fornecedor

Derivação automática do Tipo de Produto

l10n_br_tipo_produto = 'produzido'      # Default
if product.type == 'service' or product.l10n_br_codigo_servico:
    l10n_br_tipo_produto = 'servico'
elif product.type == 'consu':
    l10n_br_tipo_produto = 'consumivel'
elif product.purchase_ok:
    l10n_br_tipo_produto = 'comprado'

Derivação automática do Tipo de Cliente

l10n_br_tipo_cliente = 'pj'              # Default
if partner.l10n_br_cpf:
    l10n_br_tipo_cliente = 'pf'
if company.country_id != partner.country_id:
    l10n_br_tipo_cliente = 'ex'
if partner.l10n_br_is:                    # IS = Inscrição SUFRAMA
    l10n_br_tipo_cliente = 'zf'

Atenção: A ordem importa. Se o parceiro tem CPF e IS, prevalece zf (Zona Franca). Se tem CPF e é de outro país, prevalece ex.

Derivação automática do Destino da Operação

if company.state_id == partner.state_id:
    l10n_br_destino_operacao = '1'  # Interna
elif company.country_id != partner.country_id:
    l10n_br_destino_operacao = '3'  # Exterior
else:
    l10n_br_destino_operacao = '2'  # Interestadual

Seção 2 — Campos de Resultado (O que a Operação Define)

Quando uma operação é encontrada pelo motor, os seguintes parâmetros são aplicados automaticamente na linha do pedido ou fatura.

CFOPs por Destino

Campo Descrição Quando é usado
l10n_br_intra_cfop_id CFOP Interna company.state_id == partner.state_id
l10n_br_inter_cfop_id CFOP Interestadual Mesmo país, outro estado
l10n_br_ext_cfop_id CFOP Exterior País diferente

Determinação automática do CFOP

if company.state_id == partner.state_id:
    cfop_id = operacao.l10n_br_intra_cfop_id
elif company.country_id != partner.country_id:
    cfop_id = operacao.l10n_br_ext_cfop_id
else:
    cfop_id = operacao.l10n_br_inter_cfop_id

Documento Fiscal e Finalidade

Campo Tipo Descrição
name Char Nome da operação (identificação interna)
descricao_nf Char Natureza da operação impressa na NF (ex: "VENDA DE MERCADORIA")
l10n_br_documento_id Many2one Tipo de documento fiscal (NF-e 55, NFS-e, NFC-e 65, etc.)
l10n_br_finalidade Selection 1=Normal, 2=Complementar, 3=Ajuste, 4=Devolução, 5=Crédito, 6=Débito
l10n_br_tipo_complemento Selection Tipo de complemento (ex: preco)
l10n_br_tipo_nota_credito Selection Tipo de nota de crédito (01 a 05)
l10n_br_tipo_nota_debito Selection Tipo de nota de débito (01 a 08)

Parâmetros ICMS

Campo Tipo Descrição
l10n_br_icms_cst Selection CST do ICMS (00, 10, 20, 30, 40, 41, 50, 51, 60, 70, 90, 101–900)
l10n_br_icms_modalidade_base Selection Modalidade BC: 0=MVA, 1=Pauta, 2=Preço Tabelado, 3=Operação
l10n_br_icms_reducao_base Float Percentual de redução da base de cálculo
l10n_br_icms_aliquota Float Alíquota do ICMS (%)
l10n_br_icms_zerar_base Boolean Zera a base de cálculo (para isenções)
l10n_br_icms_remove_despesa_base Boolean Remove frete/seguro/despesas da base
l10n_br_icms_motivo_desonerado Selection Motivo de desoneração do ICMS
l10n_br_exclui_icms_piscofins Boolean Exclui ICMS da base do PIS/COFINS
l10n_br_exclui_difal_piscofins Boolean Exclui DIFAL da base do PIS/COFINS
l10n_br_icms_sempre_outros Boolean Calcula ICMS em "Outros" (ignora CST, apenas entradas)

Parâmetros ICMS ST

Campo Tipo Descrição
l10n_br_icmsst_modalidade_base Selection Modalidade BC do ICMS ST
l10n_br_icmsst_reducao_base Float Redução da BC do ICMS ST (%)
l10n_br_icmsst_mva Float MVA — Margem de Valor Agregado (%)
l10n_br_icmsst_aliquota Float Alíquota do ICMS ST (%)
l10n_br_icmsst_icmsfora Boolean Não subtrair ICMS próprio do ICMS ST
l10n_br_icmsst_add_fcp_base Boolean Adicionar FCP na base do ICMS ST
l10n_br_fcp_zero Boolean Nunca incide FCP
l10n_br_icmsst_convenio Boolean Possui convênio entre UFs (default: True)
l10n_br_icms_credito_informado Boolean Crédito de ICMS SN informado

Parâmetros IPI

Campo Descrição
l10n_br_ipi_cst CST do IPI (00–99)
l10n_br_ipi_zerar_base Zerar base de cálculo IPI (default: True)

Parâmetros PIS e COFINS

Imposto CST Alíquota Redução Base
PIS l10n_br_pis_cst l10n_br_pis_aliquota l10n_br_pis_reducao_base
COFINS l10n_br_cofins_cst l10n_br_cofins_aliquota l10n_br_cofins_reducao_base

Impostos sobre Serviços e Retenções

Campo Descrição
l10n_br_iss_aliquota Alíquota ISS (%)
l10n_br_irpj_aliquota Alíquota IRPJ (%)
l10n_br_csll_aliquota Alíquota CSLL (%)
l10n_br_irpj_ret_aliquota IRPJ Retido (%)
l10n_br_inss_ret_aliquota INSS Retido (%)
l10n_br_iss_ret_aliquota ISS Retido (%)
l10n_br_csll_ret_aliquota CSLL Retido (%)
l10n_br_pis_ret_aliquota PIS Retido (%)
l10n_br_cofins_ret_aliquota COFINS Retido (%)
l10n_br_prestao_municipio_cliente Serviço prestado no município do cliente

Reforma Tributária (IBS/CBS/IS)

Campo Descrição
l10n_br_ibscbs_cst CST do IBS/CBS
l10n_br_ibscbs_classtrib_id Classificação tributária IBS/CBS
l10n_br_ibscbs_diferido_aliquota % Alíquota Diferida
l10n_br_ibscbs_reducao_aliquota % Redução da Alíquota
l10n_br_ibs_uf_aliquota Alíquota IBS UF (%)
l10n_br_ibs_mun_aliquota Alíquota IBS Município (%)
l10n_br_cbs_aliquota Alíquota CBS (%)
l10n_br_ibscbs_substituir_ncm Substituir NCM na NF
l10n_br_is_cst CST do Imposto Seletivo
l10n_br_is_classtrib_id Classificação tributária IS
l10n_br_is_aliquota Alíquota IS (%)

Outros campos

Campo Descrição
l10n_br_ii_aliquota Alíquota do Imposto de Importação (%)
l10n_br_ii_zerar Zerar alíquota do II
l10n_br_movimento_estoque Movimenta estoque (default: True)
l10n_br_difal_regra Regra para cálculo do DIFAL
l10n_br_gera_ibpt Gerar IBPT (default: True)
l10n_br_gera_cpv Forçar geração de CPV/CMV
l10n_br_mensagem_fiscal_id Observação fiscal da operação
l10n_br_informacao_adicional_produto_id Informação adicional do produto
corredor_importacao_id Corredor de importação (parceiro)

Seção 3 — Motor de Determinação Automática

Onde é executado

O motor de determinação é executado em 3 contextos idênticos em lógica, implementados separadamente:

Contexto Arquivo Método da Linha Chamado por
Pedido de Venda sale_order.py SaleOrderLine._do_calculate_l10n_br_impostos() onchange_l10n_br_calcular_imposto()
Pedido de Compra purchase.py PurchaseOrderLine._do_calculate_l10n_br_impostos() onchange_l10n_br_calcular_imposto()
Fatura account.py AccountMoveLine._do_calculate_l10n_br_impostos() Onchange similar

Pré-condição

O motor só executa se:

if self.env.company.country_id != self.env.ref('base.br'):
    return
if self.l10n_br_imposto_auto:   # Flag de cálculo automático na linha
    ...

Algoritmo passo a passo (Vendas — sale_order.py linha 3874)

flowchart TD
    A["Início do cálculo<br/>da linha"] --> B["Preenche l10n_br_origem<br/>do produto se vazio"]
    B --> C["Monta domain base:<br/>tipo_operacao=saida<br/>tipo_pedido=X<br/>company_id=Y"]
    C --> D["Adiciona filtro<br/>por tipo_produto"]
    D --> E["Adiciona filtro<br/>por tipo_cliente"]
    E --> F["Adiciona filtros:<br/>regime_trib, orgao_pub,<br/>indicador_ie, origem"]
    F --> G["Deriva destino_operacao<br/>(interna/inter/exterior)"]
    G --> H["Deriva tipo_destinacao"]
    H --> I["Verifica NCM/UF<br/>para ICMS ST"]
    I --> J["Adiciona filtros M2M:<br/>categ, ncm, produto,<br/>tags, servico, states,<br/>municipio, partner"]
    J --> K["Adiciona filtro<br/>valor_minimo"]
    K --> L["Define ORDER BY<br/>(v1 ou v2)"]
    L --> M["Search(domain,<br/>order, limit=1)"]
    M --> N{"Encontrou?"}
    N -->|Sim| O["Grava operação<br/>na linha"]
    N -->|Não| P["operacao_id = False"]
    O --> Q["Determina CFOP<br/>(intra/inter/ext)"]
    Q --> R["Aplica mensagens<br/>fiscais"]
    R --> S["Calcula impostos<br/>(IPI, ICMS, PIS...)"]

Regra de Ordenação (ORDER BY)

A ordenação determina qual operação vence quando múltiplas operações fazem match. Existem duas versões configuráveis via company.l10n_br_regra_operacao:

Versão 1 (v1) — Prioriza critérios fiscais

ORDER BY:
  l10n_br_tipo_produto,
  l10n_br_tipo_cliente,
  l10n_br_orgao_publico,
  l10n_br_regime_tributario,
  l10n_br_indicador_ie,
  l10n_br_origem,
  l10n_br_tipo_destinacao,
  l10n_br_operacao_icmsst,
  l10n_br_destino_operacao,
  l10n_br_cfop_orig_id,
  l10n_br_valor_minimo DESC,
  categ_is_set DESC,
  ncm_is_set DESC,
  servico_is_set DESC,
  partner_state_is_set DESC,
  l10n_br_municipio_is_set DESC,
  product_is_set DESC,
  fiscal_tag_is_set DESC,
  partner_is_set DESC,
  partner_fiscal_tag_is_set DESC

Versão 2 (v2) — Prioriza especificidade do parceiro/produto (DEFAULT)

ORDER BY:
  partner_is_set DESC,
  partner_fiscal_tag_is_set DESC,
  l10n_br_municipio_is_set DESC,
  partner_state_is_set DESC,
  product_is_set DESC,
  fiscal_tag_is_set DESC,
  servico_is_set DESC,
  ncm_is_set DESC,
  categ_is_set DESC,
  l10n_br_tipo_produto,
  l10n_br_tipo_cliente,
  l10n_br_orgao_publico,
  l10n_br_regime_tributario,
  l10n_br_indicador_ie,
  l10n_br_origem,
  l10n_br_tipo_destinacao,
  l10n_br_operacao_icmsst,
  l10n_br_destino_operacao,
  l10n_br_cfop_orig_id,
  l10n_br_valor_minimo DESC

Diferença prática entre v1 e v2

Cenário v1 v2 (default)
Operação genérica (sem filtros M2M) vs. operação com parceiro específico Pode selecionar a genérica Prioriza a com parceiro
Operação com NCM específico vs. operação com produto específico Prioriza NCM Prioriza produto
Operação com tag fiscal do parceiro vs. operação com UF Prioriza UF Prioriza tag fiscal

Recomendação: A v2 é mais intuitiva para a maioria dos cenários, pois regras com parceiro ou produto específico sempre vencem sobre regras genéricas.

Configuração da versão

Definições → Empresa → Regra de Operação Campo: company.l10n_br_regra_operacao — default 'v2'

Lógica dos campos *_is_set

Campos computados que indicam se cada M2M tem registros. São stored e usados na ordenação SQL:

@api.depends('categ_ids', 'ncm_ids', 'servico_ids', 'product_ids',
             'fiscal_tag_ids', 'partner_ids', 'partner_fiscal_tag_ids',
             'partner_state_ids', 'l10n_br_municipio_ids')
def _l10n_br_m2m_set(self):
    for line in self:
        line.update({
            'categ_is_set': True if line.categ_ids else False,
            'ncm_is_set': True if line.ncm_ids else False,
            ...
        })

Efeito: Na ordenação DESC, operações que TÊM filtros M2M preenchidos (True/1) aparecem ANTES das que não têm (False/0). Isso garante que regras mais específicas vençam.

Hook de extensão: _do_get_operacao_domain

def _do_get_operacao_domain(self, domain, order):
    return domain, order

Método de extensão que permite módulos customizados alterarem o domain e a ordenação antes da busca. Override este método para adicionar critérios adicionais.

Operação manual

if self.l10n_br_operacao_manual:
    values_to_update.pop('l10n_br_operacao_id', None)

Se a flag l10n_br_operacao_manual estiver ativa na linha, o motor não substitui a operação — permite ao usuário manter uma operação selecionada manualmente.


Seção 4 — Tipos de Pedido

Saída (49 tipos)

Categoria Tipos (chave → label)
Vendas venda, servico, venda-nfce, venda_futura, venda-consignacao, venda-locacao, venda-armazem, venda-conta-ordem, venda-conta-ordem-vendedor, venda-industrializacao
Remessas bonificacao, conserto, teste, demonstracao, mostruario, deposito, fora, ativo-fora, vasilhame, feira, comodato, garantia, consignacao, locacao, uso-prestacao, rem-obra, rem-conta-ordem, rem-venda-futura, amostra, exportacao, industrializacao
Devoluções compra (dev. compra), dev-conserto, dev-teste, dev-consignacao, dev-locacao, dev-demonstracao, dev-mostruario, dev-industrializacao, dev-comodato, dev-importacao, dev-vasilhame
Outros transf-filial, baixa-estoque, complemento-valor, perda, troca, outro

Entrada (38 tipos)

Categoria Tipos
Compras compra, compra-ent-futura, compra-rec-ent-futura, compra-venda-ordem, compra-rec-venda-ordem
Importação importacao, importacao-transporte, comp-importacao
Serviços servico, credito-imposto
Retornos retorno, demonstracao, mostruario, feira, consignacao, locacao, ret-locacao, comodato, deposito, conserto, ativo-fora, vasilhame, troca
Industrialização rem-industrializacao, industrializacao, serv-industrializacao
Recebimentos ent-conserto, ent-demonstracao, ent-mostruario, ent-bonificacao, ent-amostra, ent-comodato, ent-vasilhame
Devoluções devolucao (emissão própria), devolucao_compra (dev. venda)
Outros rem-conta-ordem, transf-filial, outro

Tipos sem geração financeira

As listas TIPO_PEDIDO_SAIDA_NO_PAYMENT e TIPO_PEDIDO_ENTRADA_NO_PAYMENT definem operações que não geram contas a pagar/receber. Incluem todas as remessas, devoluções operacionais e transferências.


Seção 5 — Fluxo após a Determinação

Após encontrar a operação, o motor continua com:

1. Determinação do CFOP

Seleciona intra_cfop_id, inter_cfop_id ou ext_cfop_id conforme a UF empresa vs. parceiro.

2. Mensagens Fiscais

Se operacao.l10n_br_mensagem_fiscal_id estiver preenchida, grava l10n_br_mensagem_fiscal_ids na linha.

3. Informação Adicional do Produto

Se operacao.l10n_br_informacao_adicional_produto_id estiver preenchida, grava na linha.

4. Cálculo de IPI

  • CST do IPI: hierarquia de prioridade — Operação > CFOP > NCM > default 99
  • Base: total_nfe + icms_desonerado - icmsst - fcp_st - ipi
  • Alíquota: vem do NCM (l10n_br_ipi_aliquota)
  • Enquadramento: busca em l10n_br_ciel_it_account.ipi.enquadramento por NCM+CFOP+CST

5. Cálculo de ICMS

  • CST: da operação (sobrescreve)
  • Base: calculada conforme modalidade_base
  • Alíquota: da operação ou da tabela ICMS UF (interestadual)

6. Cálculo de PIS/COFINS

  • CST: da operação (sobrescreve)
  • Alíquota: da operação ou da empresa (configuração geral)
  • Base PIS/COFINS: valor do produto, podendo excluir ICMS da base se l10n_br_exclui_icms_piscofins = True

7. Cálculo dos demais impostos

ISS, IRPJ, CSLL, II, IBS/CBS/IS — direto da operação.

8. Observação fiscal no cabeçalho

As mensagens fiscais de todas as linhas são consolidadas no campo l10n_br_informacao_fiscal do cabeçalho, com substituição de variáveis:

Variável Substituído por
%%l10n_br_mensagem_fiscal_01_id%% a %%05%% Mensagens fiscais 01–05 do parceiro
%%l10n_br_is%% Inscrição SUFRAMA do parceiro
%%email%% E-mail do parceiro
%%name%% Nome do parceiro
%%invoice_origin%% Número do pedido/fatura
%%user_id%% Nome do vendedor
%%l10n_br_pedido_compra%% Referência de compra do cliente
%%l10n_br_icmsst_retido_base%% Base ICMS ST retido (formatado)
%%l10n_br_icmsst_retido_valor%% Valor ICMS ST retido
%%l10n_br_icms_diferido_valor%% Valor ICMS diferido
%%l10n_br_iss_valor%%, %%l10n_br_iss_ret_valor%% ISS e ISS retido
%%l10n_br_irpj_valor%%, %%l10n_br_irpj_ret_valor%% IRPJ e IRPJ retido
%%l10n_br_csll_valor%%, %%l10n_br_csll_ret_valor%% CSLL e CSLL retido
%%l10n_br_pis_valor%%, %%l10n_br_pis_ret_valor%% PIS e PIS retido
%%l10n_br_cofins_valor%%, %%l10n_br_cofins_ret_valor%% COFINS e COFINS retido
%%l10n_br_is_valor%% IS valor

Mixin de Vigência (l10n_br_ciel_it_account.vigencia.mixin)

Objetivo

O Mixin de Vigência implementa regras de validade temporal em cadastros fiscais. Permite definir uma data de início e data de fim para qualquer registro fiscal — quando ativo, o motor de determinação ignora automaticamente registros fora do período de vigência da operação.

Referência: product.py linhas 355–370 — classe L10nBrVigenciaMixin

Definição do Modelo

class L10nBrVigenciaMixin(models.AbstractModel):
    _name = 'l10n_br_ciel_it_account.vigencia.mixin'
    _description = 'Mixin de Vigência'

    l10n_br_vigencia_inicio = fields.Date(string='Início da Vigência', tracking=True)
    l10n_br_vigencia_fim = fields.Date(string='Fim da Vigência', tracking=True)

Tipo: AbstractModel — não cria tabela própria no banco de dados. Os campos são adicionados diretamente nas tabelas dos modelos que herdam o mixin.

Campos

Campo Tipo Obrigatório Descrição
l10n_br_vigencia_inicio Date Data a partir da qual o registro é válido. Se vazio, considera válido desde sempre
l10n_br_vigencia_fim Date Data até a qual o registro é válido. Se vazio, considera válido para sempre

O mixin sobrescreve o método search() do ORM para injetar automaticamente condições de vigência quando o contexto apply_vigencia_filter=True está ativo:

@api.model
def search(self, domain, offset=0, limit=None, order=None):
    if self.env.context.get('apply_vigencia_filter'):
        today = fields.Date.context_today(self)
        domain = expression.AND([domain, [
            '|', ('l10n_br_vigencia_inicio', '=', False), ('l10n_br_vigencia_inicio', '<=', today),
            '|', ('l10n_br_vigencia_fim', '=', False), ('l10n_br_vigencia_fim', '>=', today),
        ]])
    return super().search(domain, offset=offset, limit=limit, order=order)

Lógica do Filtro

O domínio injetado implementa a seguinte lógica para cada registro candidato:

Condição Regra
l10n_br_vigencia_inicio Deve ser vazio OU <= hoje
l10n_br_vigencia_fim Deve ser vazio OU >= hoje

Ambas as condições precisam ser verdadeiras (AND). Na prática:

Vigência Início Vigência Fim Resultado
Vazio Vazio ✅ Sempre válido
2025-01-01 Vazio ✅ Válido a partir de 01/01/2025, sem fim
Vazio 2026-12-31 ✅ Válido até 31/12/2026, desde sempre
2025-01-01 2026-12-31 ✅ Válido entre 01/01/2025 e 31/12/2026
2027-01-01 2027-12-31 ❌ Ainda não vigente (início futuro)
2024-01-01 2024-12-31 ❌ Expirado (fim no passado)

Data de referência: Usa fields.Date.context_today(self) — respeita o fuso horário do contexto do usuário.

flowchart TD
    A["search() chamado"] --> B{"apply_vigencia_filter\nno contexto?"}
    B -->|Não| C["Retorna TODOS\nos registros"]
    B -->|Sim| D["Injeta condição\nde vigência no domain"]
    D --> E{"inicio vazio\nOU inicio <= hoje?"}
    E -->|Não| F["❌ Exclui registro"]
    E -->|Sim| G{"fim vazio\nOU fim >= hoje?"}
    G -->|Não| F
    G -->|Sim| H["✅ Inclui registro"]

Modelos que Herdam o Mixin

O mixin é aplicado em 8 modelos fiscais que possuem regras que podem variar ao longo do tempo:

# Model Classe Arquivo Propósito
1 l10n_br_ciel_it_account.operacao L10nBrOperacao sale_order.py:5549 Operações Fiscais — regras de CFOP, CST e alíquotas
2 l10n_br_ciel_it_account.ncm.uf L10nBrNcmUf product.py:608 NCM por UF — alíquotas ICMS/ST por UF de origem/destino
3 l10n_br_ciel_it_account.ncm.origem.uf L10nBrNcmOrigemUf product.py:640 NCM por Origem e UF — alíquotas por origem da mercadoria + UF
4 l10n_br_ciel_it_account.ncm.ie.uf L10nBrNcmIeUf product.py:672 NCM por Indicador de IE e UF — regras por tipo de contribuinte
5 l10n_br_ciel_it_account.ncm.cliente.uf L10nBrNcmClienteUf product.py:706 NCM por UF e Cliente — regras específicas por parceiro
6 l10n_br_ciel_it_account.ex.tarifario L10nBrExTarifario product.py:755 EX-Tarifário — exceções tarifárias por NCM/produto/fornecedor
7 l10n_br_ciel_it_account.icms.beneficio L10nBrIcmsBeneficio product.py:779 Benefícios Fiscais ICMS — códigos de benefício por NCM+CFOP+CST
8 l10n_br_ciel_it_account.ipi.enquadramento L10nBrIpiEnquadramento product.py:888 Enquadramento IPI — códigos de enquadramento por NCM+CFOP+CST

Nota: Os modelos l10n_br_ciel_it_account.icms.uf (ICMS por UF), l10n_br_ciel_it_account.ncm (NCM) e l10n_br_ciel_it_account.icms.ajuste (Ajustes ICMS) não herdam o mixin, pois representam tabelas de referência que não possuem vigência temporal.

Pontos de Ativação do Filtro

O contexto apply_vigencia_filter=True é ativado nos seguintes pontos do cálculo fiscal:

# Arquivo Linha Método Contexto
1 sale_order.py 675 _do_calculate_l10n_br_impostos() Cálculo de impostos no pedido de venda
2 sale_order.py 2742 Cálculo SO (segundo ponto) Recálculo em alterações de linha
3 sale_order.py 3882 Cálculo SO (terceiro ponto) Recálculo em confirmação
4 purchase.py 305 _do_calculate_l10n_br_impostos() Cálculo de impostos no pedido de compra
5 purchase.py 1535 Cálculo PO (segundo ponto) Recálculo em linhas de compra
6 account.py 7498 _do_calculate_l10n_br_impostos() Cálculo de impostos na fatura
7 account.py 9595 Cálculo fatura (segundo ponto) Recálculo ao postar/editar fatura

A ativação é feita via:

self = self.with_context(apply_vigencia_filter=True)

A partir deste ponto, qualquer search() nos 8 modelos listados acima aplicará automaticamente o filtro de vigência. Isso significa que:

  • A busca de Operações Fiscais ignora operações expiradas ou futuras
  • A busca de NCM/UF ignora regras de ICMS/ST fora do período
  • A busca de Benefícios ignora benefícios expirados
  • A busca de Enquadramentos IPI ignora enquadramentos fora da vigência
  • A busca de EX-Tarifário ignora exceções tarifárias vencidas

Exemplo Prático

Cenário: Mudança de alíquota de ICMS em 01/04/2026

  1. Criar a regra vigente (NCM/UF):
  2. l10n_br_vigencia_inicio = vazio (ou data antiga)
  3. l10n_br_vigencia_fim = 2026-03-31
  4. l10n_br_icms_aliquota = 18.00%

  5. Criar a nova regra (NCM/UF):

  6. l10n_br_vigencia_inicio = 2026-04-01
  7. l10n_br_vigencia_fim = vazio
  8. l10n_br_icms_aliquota = 20.50%

  9. Resultado:

  10. Pedidos criados antes de 01/04 usam a regra com 18%
  11. Pedidos criados a partir de 01/04 usam a regra com 20.5%
  12. Ambas as regras coexistem no banco sem conflito

Cenário: Operação fiscal temporária (campanha promocional)

  1. Operação padrão: Vigência vazia (sempre ativa)
  2. Operação promocional: l10n_br_vigencia_inicio = 2026-06-01, l10n_br_vigencia_fim = 2026-06-30
  3. Durante junho, o motor encontra ambas as operações — a mais específica vence pela ordenação padrão

Interface (Views XML)

Os campos de vigência são expostos nas views de search (filtro), tree (listagem) e form (formulário) de todos os modelos que herdam o mixin.

Filtros de busca disponíveis: - Filtro "Vigente Hoje" — mostra apenas registros com vigência ativa - Campos de busca por Início e Fim da Vigência

Troubleshooting da Vigência

Sintoma Causa Provável Solução
Operação não encontrada em novo pedido Vigência expirada Verificar l10n_br_vigencia_fim da operação
Regra antiga sendo aplicada Nova regra com l10n_br_vigencia_inicio futuro Ajustar a data de início
Vigência não sendo respeitada Contexto apply_vigencia_filter não ativado Verificar se o ponto de entrada chama with_context(apply_vigencia_filter=True)
Registros invisíveis na listagem Filtro de vigência ativo no search() sem o contexto Acessar via menu (sem contexto) — todos os registros aparecem
Duas regras coexistem e a errada vence Ordenação da operação prioriza a menos específica Ajustar critérios de matching (M2M, tipo produto, etc.) para desempate

TTD — Tratamento Tributário Diferenciado (Crédito Presumido / Ajuste de ICMS)

Conceito Funcional

O Tratamento Tributário Diferenciado (TTD) é um mecanismo pelo qual a empresa emite notas fiscais com o ICMS cheio (valor integral), mas na apuração do SPED Fiscal declara um crédito presumido que reduz o valor efetivamente devido. Isso resulta em dois valores distintos por linha de fatura:

Conceito Campo Fórmula
ICMS Tributável l10n_br_icms_valor Base × Alíquota ICMS — valor cheio na NF-e
Crédito Presumido l10n_br_icms_valor_credito_presumido Soma dos ajustes com l10n_br_transf_credito = True
ICMS Efetivo l10n_br_icms_valor_efetivo ICMS Tributável - Crédito Presumido

Na prática: A NF-e vai para a SEFAZ com o ICMS cheio. O benefício só é declarado na apuração fiscal (SPED), onde o sistema gera registros C195/C197 e ajusta os totais do E110 e Registro 1920.

Arquitetura de Models

O sistema utiliza 3 modelos interligados em uma hierarquia:

erDiagram
    ICMS_AJUSTE ||--o{ ICMS_AJUSTE_ALIQUOTA : "regra de matching"
    ICMS_AJUSTE ||--o{ ICMS_AJUSTE_LINE : "resultado por linha"
    ICMS_AJUSTE_LINE }o--|| ACCOUNT_MOVE_LINE : "move_line_id"

    ICMS_AJUSTE {
        char l10n_br_codigo
        char l10n_br_nome
        char l10n_br_descricao
        bool l10n_br_transf_credito
        bool l10n_br_transf_debito
        char l10n_br_codigo_ttd
        char l10n_br_numero_concessao
        float l10n_br_fumdes_aliquota
        selection l10n_br_fundosocial_modo
        float l10n_br_fundosocial_aliquota
    }

    ICMS_AJUSTE_ALIQUOTA {
        selection l10n_br_tipo_operacao
        date date_start
        date date_end
        many2one l10n_br_ajuste_id
        float l10n_br_icms_aliquota
    }

    ICMS_AJUSTE_LINE {
        many2one move_line_id
        many2one l10n_br_ajuste_id
        float l10n_br_icms_base
        float l10n_br_icms_aliquota
        float l10n_br_icms_valor
    }

Model 1 — Código de Ajuste ICMS (l10n_br_ciel_it_account.icms.ajuste)

Arquivo: product.py linhas 795–818 Menu: Estoque → Configuração → ICMS - Código do Ajuste

Este model representa o código de ajuste oficial da SEFAZ. Define as propriedades do benefício e os parâmetros de cálculo para FUMDES e Fundo Social (específico SC).

Campo Tipo Descrição
l10n_br_codigo Char Código oficial do ajuste SEFAZ (ex: SC020002)
l10n_br_nome Char Descrição do ajuste
l10n_br_descricao Char Descrição complementar (usada no C195)
l10n_br_transf_credito Boolean Marca que este ajuste gera transferência de crédito (crédito presumido)
l10n_br_transf_debito Boolean Marca que este ajuste gera transferência de débito
l10n_br_codigo_ttd Char Código do benefício TTD (usado no DIME SC Quadro 15)
l10n_br_numero_concessao Char Número da concessão TTD
l10n_br_fumdes_aliquota Float Alíquota do FUMDES (%) — FUMDES = Crédito Presumido × Alíquota
l10n_br_fundosocial_modo Selection Modo de cálculo do Fundo Social: regra1 ou regra2
l10n_br_fundosocial_aliquota Float Alíquota do Fundo Social (%)

Flags l10n_br_transf_credito e l10n_br_transf_debito

Essas flags são os diferenciadores centrais do mecanismo. Determinam como o valor do ajuste impacta a apuração do ICMS:

Flag Impacto na Apuração Registro SPED
l10n_br_transf_credito = True Soma no c197_vl_tot_transf_creditos + e110_vl_aj_debitos C197 → E110.VL_AJ_DEBITOS
l10n_br_transf_debito = True Soma no c197_vl_tot_transf_debitos C197 → Registro 1920.VL_TOT_TRANSF_DEBITOS_OA
Ambas False Soma no e110_vl_aj_creditos C197 → E110.VL_AJ_CREDITOS

Atenção: Um ajuste com l10n_br_transf_credito = True soma simultaneamente em c197_vl_tot_transf_creditos (para o Registro 1920) e em e110_vl_aj_debitos (para o E110).

Modos de Cálculo do Fundo Social

Modo Fórmula Uso típico
regra1 FUNDO SOCIAL = (Base × Alíquota Fundo Social) - FUMDES Padrão SC — base é a base de cálculo do ICMS
regra2 FUNDO SOCIAL = Crédito Presumido × Alíquota Fundo Social Alternativo — base é o próprio valor do crédito presumido

Model 2 — Regra de Matching / Alíquota (l10n_br_ciel_it_account.icms.ajuste.aliquota)

Arquivo: product.py linhas 805–868 Menu: Estoque → Configuração → ICMS - Alíquotas do Ajuste

Este model define quando e com qual alíquota um ajuste se aplica. Funciona como uma tabela de determinação similar à operação fiscal, com múltiplos critérios de matching.

Campo Tipo Descrição Match
company_id Many2one Empresa Exato
l10n_br_tipo_operacao Selection saida / entrada Exato
date_start Date Data inicial da regra (obrigatório) date_start <= invoice_date
date_end Date Data final da regra (opcional) date_end >= invoice_date OR vazio
state_de_id Many2one UF de origem Match ou wildcard
state_para_id Many2one UF de destino Match ou wildcard
l10n_br_ncm_ids Many2many NCMs Match ou wildcard
l10n_br_tipo_produto Selection Tipo de produto Match ou wildcard
l10n_br_origem Selection Origem da mercadoria Match ou wildcard
l10n_br_indicador_ie Selection Indicador de IE do parceiro Match ou wildcard
l10n_br_cfop_ids Many2many CFOPs Match ou wildcard
l10n_br_icms_cst Selection CST do ICMS Match ou wildcard
l10n_br_icms_usar_aliquota Boolean Se filtra pela alíquota do ICMS Condicional
l10n_br_icms_aliquota_filtro Float Alíquota de ICMS para filtrar Se usar_aliquota = True
l10n_br_ajuste_id Many2one Código do Ajuste ICMS vinculado
l10n_br_icms_aliquota Float Alíquota do ajuste (%)
l10n_br_icms_difal Boolean Se é diferencial de alíquota
is_regra_python Boolean Se usa fórmula Python personalizada
code Text Código Python (quando is_regra_python = True)

Fórmula Python Personalizada

Quando is_regra_python = True, o campo code pode conter uma fórmula Python que retorna um dicionário com os valores calculados:

# Retorno esperado:
return {
    'l10n_br_icms_base': 0.0,
    'l10n_br_icms_aliquota': 0.0,
    'l10n_br_icms_valor': 0.00,
    'l10n_br_icms_valor_outros': 0.00
}

Nota: O campo code é restrito ao grupo base.group_system (Administrador de Sistema).


Model 3 — Linha de Ajuste (l10n_br_ciel_it_account.icms.ajuste.line)

Arquivo: account.py linhas 20851–20868

Este model armazena o resultado do cálculo de ajuste para cada linha de fatura. É criado pelo método action_gerar_ajuste_icms().

Campo Tipo Descrição
move_line_id Many2one → account.move.line Linha de fatura vinculada
move_id Many2one → account.move Fatura (related)
partner_id Many2one → res.partner Parceiro (related)
product_id Many2one → product.product Produto (related)
l10n_br_icms_cst Selection CST ICMS (related da linha)
l10n_br_ajuste_id Many2one → icms.ajuste Código do ajuste aplicado
l10n_br_icms_base Float Base de cálculo do ajuste
l10n_br_icms_aliquota Float Alíquota do ajuste
l10n_br_icms_valor Float Valor do ajuste
l10n_br_icms_valor_outros Float Valor de outros ajustes

Motor de Geração — action_gerar_ajuste_icms()

Arquivo: account.py linhas 1014–1109
Invocação: Pode ser chamado manualmente na fatura ou automaticamente pelo SPED Fiscal quando c197_gerar_apuracao_ajustes_icms = True.

Pré-condições

Condição Efeito se False
l10n_br_tipo_documento == '55' Retorna sem fazer nada (só gera ajuste para NF-e modelo 55)
l10n_br_icms_base > 0.00 por linha Linhas sem base de ICMS são ignoradas

Fluxo de Execução

flowchart TD
    A["action_gerar_ajuste_icms()"] --> B["Apagar ajustes existentes\n(l10n_br_icms_ajuste_ids.unlink)"]
    B --> C["Para cada linha com\nl10n_br_icms_base > 0"]
    C --> D["Montar domain dinâmico\n(10 critérios)"]
    D --> E["search icms.ajuste.aliquota\n(com ordenação por especificidade)"]
    E --> F{"Encontrou\nregra(s)?"}
    F -->|Não| G["Próxima linha"]
    F -->|Sim| H["Para cada regra encontrada:\ncriar icms.ajuste.line"]
    H --> I["valor = base × aliquota / 100"]
    I --> G
    G --> J["_l10n_br_credito_presumido()\nrecalcula totais"]

Critérios do Domain (em ordem)

# Critério Campo pesquisado Lógica
1 company_id Empresa da fatura Exato
2 l10n_br_tipo_operacao Derivado de TIPO_NF_OPERACAO[move_type] Exato
3 date_start / date_end invoice_date da fatura date_start <= data AND (date_end >= data OR vazio)
4 state_de_id UF da empresa (company.state_id) Match ou wildcard
5 state_para_id UF do parceiro (partner.state_id) Match ou wildcard
6 l10n_br_ncm_ids NCM do produto Match ou wildcard
7 l10n_br_tipo_produto Derivado do produto (mesma lógica da operação fiscal) Match ou wildcard
8 l10n_br_origem Origem do produto (line.l10n_br_origem) Match ou wildcard
9 l10n_br_cfop_ids CFOP da linha (line.l10n_br_cfop_id) Match ou wildcard
10 l10n_br_indicador_ie Indicador IE do parceiro Match ou wildcard
11 l10n_br_icms_cst CST ICMS da linha Match ou wildcard
12 l10n_br_icms_aliquota_filtro Alíquota ICMS da linha Só se l10n_br_icms_usar_aliquota = True

Ordenação das Regras

state_de_id, state_para_id, l10n_br_tipo_produto, l10n_br_origem, 
l10n_br_indicador_ie, l10n_br_icms_cst, ncm_is_set desc, cfop_is_set desc

Nota importante: Diferente da operação fiscal (que retorna apenas a primeira), o motor de ajustes aplica TODAS as regras encontradas. Cada regra gera uma entrada icms.ajuste.line separada. Isso permite cenários onde múltiplos ajustes coexistem para a mesma linha.

Cálculo aplicado por ajuste encontrado

line_id.l10n_br_icms_ajuste_ids = [(0, 0, {
    'l10n_br_ajuste_id': ajuste_aliquota_id.l10n_br_ajuste_id.id,
    'l10n_br_icms_base': line_id.l10n_br_icms_base,             # Base = base ICMS da linha
    'l10n_br_icms_aliquota': ajuste_aliquota_id.l10n_br_icms_aliquota,  # Alíquota da regra
    'l10n_br_icms_valor': line_id.l10n_br_icms_base * ajuste_aliquota_id.l10n_br_icms_aliquota / 100.0,
    'l10n_br_icms_valor_outros': 0.0,
})]

Computed Fields — _l10n_br_credito_presumido()

Arquivo: account.py linhas 8517–8524

Após a geração dos ajustes, os computed fields na account.move.line são automaticamente recalculados:

@api.depends('l10n_br_icms_ajuste_ids.l10n_br_icms_valor', 'l10n_br_icms_ajuste_ids.l10n_br_ajuste_id')
def _l10n_br_credito_presumido(self):
    for line in self:
        line.l10n_br_icms_valor_credito_presumido = 0.0
        for l10n_br_icms_ajuste_id in line.l10n_br_icms_ajuste_ids:
            if l10n_br_icms_ajuste_id.l10n_br_ajuste_id.l10n_br_transf_credito:
                line.l10n_br_icms_valor_credito_presumido += l10n_br_icms_ajuste_id.l10n_br_icms_valor
        line.l10n_br_icms_valor_efetivo = line.l10n_br_icms_valor - line.l10n_br_icms_valor_credito_presumido

Lógica

  1. Inicia com zero: l10n_br_icms_valor_credito_presumido = 0.0
  2. Soma apenas ajustes com l10n_br_transf_credito = True: Ajustes de débito ou sem flag não entram no crédito presumido
  3. Calcula valor efetivo: ICMS Efetivo = ICMS Tributável - Crédito Presumido

Campos no account.move.line

Campo Compute Descrição
l10n_br_icms_valor_credito_presumido _l10n_br_credito_presumido Soma dos valores de ajustes com transf_credito = True
l10n_br_icms_valor_efetivo _l10n_br_credito_presumido l10n_br_icms_valor - l10n_br_icms_valor_credito_presumido

Campos no account.move (Totalizadores)

Campo Compute Descrição
l10n_br_icms_valor_credito_presumido _l10n_br_amount_all sum(invoice_line_ids.l10n_br_icms_valor_credito_presumido)
l10n_br_icms_valor_efetivo _l10n_br_amount_all sum(invoice_line_ids.l10n_br_icms_valor_efetivo)

Exemplo Numérico

Dado Valor
Base ICMS (l10n_br_icms_base) R$ 10.000,00
Alíquota ICMS na NF-e (l10n_br_icms_aliquota) 17%
ICMS Tributável (l10n_br_icms_valor) R$ 1.700,00
Alíquota do Ajuste (TTD) 5%
Crédito Presumido (l10n_br_icms_valor_credito_presumido) R$ 500,00 (10.000 × 5%)
ICMS Efetivo (l10n_br_icms_valor_efetivo) R$ 1.200,00 (1.700 - 500)

Na NF-e, vai destacado R$ 1.700,00 de ICMS. Na apuração SPED, o sistema declara R$ 500,00 como crédito presumido, resultando em imposto efetivo de R$ 1.200,00.


Impacto no SPED Fiscal

O sistema de ajustes de ICMS alimenta 4 registros do SPED Fiscal:

1. Registro C195 — Observações do Lançamento Fiscal

Gerado para cada grupo de ajustes com mesma descrição (l10n_br_descricao).

Campo SPED Origem Descrição
COD_OBS 20000 + l10n_br_ajuste_id Código da observação
TXT_COMPL l10n_br_ajuste_id.l10n_br_descricao (máx. 250 chars) Texto complementar

Referência: account_sped_fiscal.py linhas 1459–1464

2. Registro C197 — Outras Obrigações Tributárias, Ajustes e Informações

Gerado para cada linha de ajuste (icms.ajuste.line).

Campo SPED Origem Descrição
COD_AJ l10n_br_ajuste_id.l10n_br_codigo Código do ajuste SEFAZ
DESCR_COMPL_AJ l10n_br_ajuste_id.l10n_br_descricao Descrição complementar
COD_ITEM product.default_code Código do produto
VL_BC_ICMS l10n_br_icms_base do ajuste Base de cálculo
ALIQ_ICMS l10n_br_icms_aliquota do ajuste Alíquota aplicada
VL_ICMS l10n_br_icms_valor do ajuste Valor do ajuste
VL_OUTROS l10n_br_icms_valor_outros Valor de outros

Referência: account_sped_fiscal.py linhas 1491–1506

Acumulação nos Totais

Após gravar cada C197, o sistema acumula os valores nos totais da apuração:

if l10n_br_transf_credito:
    self.c197_vl_tot_transf_creditos += valor   # → Registro 1920
    self.e110_vl_aj_debitos += valor             # → E110 (ajustes a débito)
else:
    self.e110_vl_aj_creditos += valor            # → E110 (ajustes a crédito)

if l10n_br_transf_debito:
    self.c197_vl_tot_transf_debitos += valor     # → Registro 1920

3. Registro E110 — Apuração do ICMS — Operações Próprias

Os ajustes de TTD impactam diretamente os campos de apuração:

Campo E110 Fórmula da Apuração
VL_TOT_DEBITOS ICMS total das saídas
VL_AJ_DEBITOS ← Recebe os ajustes com transf_credito = True
VL_TOT_AJ_DEBITOS Ajustes manuais de débito
VL_ESTORNOS_CRED Estornos de crédito
VL_TOT_CREDITOS ICMS total das entradas
VL_AJ_CREDITOS ← Recebe os ajustes sem transf_credito e sem transf_debito
VL_SLD_CREDOR_ANT Saldo credor do período anterior

Saldo Apurado:

VL_SLD_APURADO = (VL_TOT_DEBITOS + VL_AJ_DEBITOS + VL_TOT_AJ_DEBITOS + VL_ESTORNOS_CRED)
               - (VL_TOT_CREDITOS + VL_AJ_CREDITOS + VL_TOT_AJ_CREDITOS + VL_ESTORNOS_DEB + VL_SLD_CREDOR_ANT)

4. Registro 1920 — Sub-apuração do ICMS (Outra Apuração)

Os totais de transferência alimentam uma apuração separada:

Campo 1920 Origem
VL_TOT_TRANSF_DEBITOS_OA c197_vl_tot_transf_debitos
VL_TOT_TRANSF_CREDITOS_OA c197_vl_tot_transf_creditos
VL_SLD_CREDOR_ANT_OA Saldo credor anterior (busca automática do mês anterior)
VL_ICMS_RECOLHER_OA (DEBITOS - CREDITOS - AJ_CREDITOS) - SLD_CREDOR_ANT
VL_SLD_CREDOR_TRANSP_OA Se resultado negativo, transporta como saldo credor

Referência: account_sped_fiscal.py linhas 2349–2384


Impacto na DIME SC (Santa Catarina)

Para empresas de Santa Catarina, os ajustes de TTD alimentam adicionalmente a DIME (Declaração do ICMS e do Movimento Econômico):

Quadro 14 — Sub-apuração TTD

Item DIME Descrição Origem
010 Base de cálculo Soma l10n_br_icms_base dos ajustes de débito
020 Valor do ICMS Soma l10n_br_icms_valor dos ajustes de débito
030 Valor do crédito Soma l10n_br_icms_valor dos ajustes de crédito
040 Resultado (debit - credit) Diferença
110 Saldo credor anterior x1900_vl_sld_credor_ant
120 Ajustes de apuração Soma Registros 1921
198 / 199 Saldo credor / devedor Resultado final

Quadro 15 — Registro 36 (TTD detalhado por Benefício)

Detalhamento por código TTD com cálculos de FUMDES e Fundo Social:

Campo DIME Origem
Código do Benefício TTD l10n_br_ajuste_id.l10n_br_codigo_ttd
Número Concessão TTD l10n_br_ajuste_id.l10n_br_numero_concessao
Base de Cálculo Soma l10n_br_icms_base dos ajustes com transf_credito
ICMS Exonerado Soma l10n_br_icms_valor dos ajustes com transf_credito
Valor FUMDES ICMS Exonerado × fumdes_aliquota / 100
Valor Fundo Social Depende do modo de cálculo

Fórmulas FUMDES e Fundo Social

# FUMDES — sempre igual
vFUMDES = sum(credito_presumido × (fumdes_aliquota / 100))

# Fundo Social — depende do modo
if fundosocial_modo == 'regra1':
    vSOCIAL = (base_icms × fundosocial_aliquota / 100) - vFUMDES
elif fundosocial_modo == 'regra2':
    vSOCIAL = credito_presumido × (fundosocial_aliquota / 100)

O Registro 36 gera uma linha de totalização (sequência 999) somando todos os benefícios.

Configurações da DIME

Campo DIME Tipo Descrição
calculo_fumdes Selection 0 = Sem exigibilidade, 1 = Com exigibilidade
calculo_social Selection 04, modos de cálculo do Fundo Social
dcip_credito_presumido Char Identificação do regime (Quadro 46)

Fluxo de Ativação no SPED Fiscal

O campo c197_gerar_apuracao_ajustes_icms (Boolean) no SPED Fiscal controla se o motor de ajustes é executado automaticamente durante a geração:

flowchart TD
    A["SPED Fiscal:\nGerar C190"] --> B{"c197_gerar_apuracao\n_ajustes_icms?"}
    B -->|Não| C["Gera C195/C197 apenas\npara benefícios (cBenef)"]
    B -->|Sim| D["Chama action_gerar_ajuste_icms()\npara cada NF-e do período"]
    D --> E["Registra NF-e em c197_move_ids"]
    E --> F["Gera C195 por grupo\nde descrição"]
    F --> G["Gera C197 por\nlinha de ajuste"]
    G --> H["Acumula em E110\ne Registro 1920"]
    H --> I["Gera Quadro 15\nDIME SC (se aplicável)"]

Referência: account_sped_fiscal.py linhas 1447–1466

Nota sobre SC: Existe uma exceção: para empresas de SC (company.state_id.code == 'SC'), o registro C197 de benefícios fiscais (l10n_br_codigo_beneficio) não é gerado — apenas os ajustes de alíquota passam pelo C197 (linha 1442).


Troubleshooting — TTD

Sintoma Causa Provável Solução
Crédito presumido não aparece na fatura action_gerar_ajuste_icms() não foi executado Executar manualmente na fatura ou ativar c197_gerar_apuracao_ajustes_icms no SPED
ICMS Efetivo igual ao Tributável Nenhum ajuste com transf_credito = True encontrado Verificar regras em Ajustes ICMS — Alíquotas (data, UF, NCM, CST)
SPED sem C197 c197_gerar_apuracao_ajustes_icms desativado no SPED Ativar flag antes de gerar
Múltiplos ajustes na mesma linha O motor aplica todas as regras encontradas Revisar filtros das regras para evitar sobreposição indesejada
FUMDES / Fundo Social zerados na DIME l10n_br_fumdes_aliquota ou l10n_br_fundosocial_aliquota não configurados no Ajuste Preencher alíquotas no cadastro do Código de Ajuste
E110 VL_AJ_DEBITOS muito alto Ajustes com transf_credito somando no campo errado Verificar se a flag l10n_br_transf_credito está correta no cadastro de ajuste
Valor do ajuste incorreto Alíquota errada na regra de matching Verificar icms.ajuste.aliquota — campo l10n_br_icms_aliquota
Ajuste não gerado para linhas sem ICMS l10n_br_icms_base == 0.00 é pré-condição Verificar por que a base ICMS está zerada (CST isento?)

Cenários de Uso / Troubleshooting

1. "Nenhuma operação encontrada"

  • Causa: Não existe regra que combine todos os critérios da transação
  • Diagnóstico: Ativar log com _ciel_logger (já implementado no código)
  • Verificar: tipo_operacao, tipo_pedido, company_id — esses são filtros OBRIGATÓRIOS (sem wildcard)
  • Solução: Criar operação com os critérios corretos, ou operação genérica (todos os campos opcionais vazios)

2. Operação errada sendo selecionada

  • Causa: A ordenação não prioriza a regra mais específica
  • Verificar: company.l10n_br_regra_operacao — v1 ou v2
  • Verificar: Campos M2M preenchidos vs. vazios — uma operação com partner_ids preenchido deve vencer sobre uma genérica
  • Verificar: Se os campos *_is_set estão computados corretamente (recomputar via shell se necessário)

3. CFOP incorreto na NF

  • Causa: CFOPs intra/inter/ext trocados na operação
  • Verificar: Os 3 campos de CFOP na operação
  • Verificar: UF da empresa vs. UF do parceiro — pode estar usando l10n_br_endereco_entrega_id em vez do partner_id principal

4. CST de IPI sobreescrito

  • Prioridade: operacao.l10n_br_ipi_cst > cfop.l10n_br_ipi_cst > ncm.l10n_br_ipi_cst > default '99'
  • Se a operação tem CST de IPI preenchido, ele sempre vence

5. Operação não está sendo substituída ao recalcular

  • Causa: l10n_br_operacao_manual = True na linha
  • Solução: Desmarcar a flag para permitir redeterminação automática

Referências Cruzadas