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— classeL10nBrOperacao - 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) — adicional10n_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¶
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 exato — tipo_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, prevaleceex.
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¶
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¶
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.enquadramentopor 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.pylinhas 355–370 — classeL10nBrVigenciaMixin
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 |
Mecanismo de Filtro — Override do search()¶
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) el10n_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:
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¶
- Criar a regra vigente (NCM/UF):
l10n_br_vigencia_inicio= vazio (ou data antiga)l10n_br_vigencia_fim=2026-03-31-
l10n_br_icms_aliquota=18.00% -
Criar a nova regra (NCM/UF):
l10n_br_vigencia_inicio=2026-04-01l10n_br_vigencia_fim= vazio-
l10n_br_icms_aliquota=20.50% -
Resultado:
- Pedidos criados antes de 01/04 usam a regra com 18%
- Pedidos criados a partir de 01/04 usam a regra com 20.5%
- Ambas as regras coexistem no banco sem conflito
Cenário: Operação fiscal temporária (campanha promocional)¶
- Operação padrão: Vigência vazia (sempre ativa)
- Operação promocional:
l10n_br_vigencia_inicio = 2026-06-01,l10n_br_vigencia_fim = 2026-06-30 - 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 = Truesoma simultaneamente emc197_vl_tot_transf_creditos(para o Registro 1920) e eme110_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 grupobase.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.lineseparada. 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¶
- Inicia com zero:
l10n_br_icms_valor_credito_presumido = 0.0 - Soma apenas ajustes com
l10n_br_transf_credito = True: Ajustes de débito ou sem flag não entram no crédito presumido - 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 | 0–4, 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_idspreenchido deve vencer sobre uma genérica - Verificar: Se os campos
*_is_setestã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_idem vez dopartner_idprincipal
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 = Truena linha - Solução: Desmarcar a flag para permitir redeterminação automática
Referências Cruzadas¶
- Anterior: Produtos
- Próximo: Cálculo de Impostos — usa as operações para determinar CST e alíquotas
- Vendas: Pedidos de Venda — executa
_do_calculate_l10n_br_impostosbaseado nas operações - Compras: Pedidos de Compra — lógica simétrica
- Índice: Sumário Geral