ECD — Regras Gerais de Geração¶
Mecanismo de Habilitação de Registros¶
Todos os registros da ECD passam pela verificação:
def _registro_em_uso(self, registro):
return registro in self.company_id.l10n_br_sped_ecd_registros.mapped('name')
Como funciona¶
| Aspecto | Detalhe |
|---|---|
| Campo da empresa | l10n_br_sped_ecd_registros (Many2many → l10n_br_ciel_it.sped.registro) |
| Menu | Empresa → aba Localização BR → Registros SPED Contábil (ECD) |
| Efeito | Se o registro não está na lista, o método retorna imediatamente sem gerar nada |
| Tipo de regra | ⚙️ Parametrização |
O que acontece quando um registro não está habilitado¶
| Registro | Impacto se desabilitado |
|---|---|
0000 |
Arquivo sem abertura — invalida todo o SPED |
0001 |
Bloco 0 sem indicador de dados |
0007 |
Sem inscrição estadual no arquivo |
I050 |
Sem plano de contas — invalida todo o SPED |
I155 |
Sem balancetes mensais |
I200/I250 |
Sem lançamentos — ECD fica sem Livro Diário |
I355 |
Sem saldos de resultado |
J100 |
Sem Balanço Patrimonial |
J150 |
Sem DRE |
J930 |
Sem signatário — invalida a ECD |
Regra funcional: A maioria dos registros é obrigatória para uma ECD válida. Desabilitar registros é útil apenas para depuração ou geração parcial de teste.
Riscos de configuração incorreta¶
| Risco | Consequência | Como prevenir |
|---|---|---|
| Nenhum registro habilitado | Arquivo vazio — rejeição no PVA | Verificar lista de registros na empresa |
| Registro 0000 desabilitado | Arquivo sem cabeçalho | Sempre manter habilitado |
| I050 desabilitado sem I155 | Balancete sem plano de contas | Habilitar ambos ou nenhum |
| J930 desabilitado | Sem signatário | Sempre manter habilitado |
Seleção de Contas e Grupos¶
O método _get_account_and_group_ids() (linha 107) é executado antes de qualquer registro, para determinar quais contas e grupos serão processados.
Lógica de seleção¶
flowchart TD
A["_get_account_and_group_ids()"] --> B["Consultar saldos ANTES do período"]
B --> C["read_group: contas com date < date_ini, posted"]
C --> D{"saldo_inicial ≠ 0?"}
D -->|"Sim"| E["Incluir conta + toda a hierarquia de grupos"]
D -->|"Não"| F["Ignorar"]
A --> G["Consultar movimentações DO período"]
G --> H["read_group: contas com date_ini ≤ date ≤ date_fim, posted"]
H --> I{"conta encontrada?"}
I -->|"Sim"| E
I -->|"Não"| F
E --> J["Retorna: account_ids, group_ids"]
Regras detalhadas¶
| Critério | Filtro | Tipo de regra |
|---|---|---|
| Empresa | company_id = empresa selecionada |
🔒 Fixo |
| Status | move_id.state = 'posted' |
🔒 Fixo — somente lançamentos postados |
| Saldo inicial ≠ 0 | debit - credit ≠ 0 antes de date_ini |
🧮 Calculado |
| Movimentação no período | Qualquer debit ou credit entre date_ini e date_fim |
🧮 Calculado |
| Hierarquia de grupos | Para cada conta incluída, percorre group_id → parent_id até a raiz |
🧮 Automático |
Consequência: Uma conta que nunca teve saldo e não teve movimentação no período NÃO aparece na ECD. Isso é correto e intencional — a ECD só deve conter contas utilizadas.
Por que uma conta pode não aparecer¶
| Motivo | Como verificar |
|---|---|
| Conta nunca teve movimentação | Verificar se há algum account.move.line para essa conta |
| Movimentações não postadas | Verificar move_id.state — deve ser 'posted' |
| Conta de outra empresa | Verificar company_id da conta |
| Saldo zerado e sem movimento | Correto — comportamento esperado |
Tratamento do Período¶
Período anual¶
O período da ECD é definido pelos campos date_ini e date_fim do wizard:
| Campo | Uso típico | Efeito |
|---|---|---|
date_ini |
01/01/AAAA |
Define o início dos movimentos e saldos |
date_fim |
31/12/AAAA |
Define o fim dos movimentos e demonstrativos |
Geração mensal automática (I150)¶
O sistema divide automaticamente o período anual em sub-períodos mensais para os registros I150/I155:
gerar_range_mensal(data_inicio, data_fim) → [(01/Jan, 31/Jan), (01/Fev, 28/Fev), ..., (01/Dez, 31/Dez)]
Cada par (primeiro_dia, último_dia) gera um registro I150 com seus I155 correspondentes.
Cálculo de Saldos¶
A ECD utiliza o conceito de saldo inicial, movimentação e saldo final em vários registros:
Fórmula padrão¶
Saldo Inicial = sum(debit) - sum(credit) ONDE date < data_inicio AND posted
Débitos Mês = sum(debit) ONDE data_inicio ≤ date ≤ data_fim AND posted
Créditos Mês = sum(credit) ONDE data_inicio ≤ date ≤ data_fim AND posted
Saldo Final = Saldo Inicial + Débitos - Créditos
Indicador D/C¶
| Valor | Significado | Quando |
|---|---|---|
'D' |
Débito | Saldo > 0 (natureza devedora) |
'C' |
Crédito | Saldo ≤ 0 (natureza credora) |
Regra fixa no código:
'D' if valor > 0 else 'C'. O valor absoluto é gravado no campo numérico.
Lançamentos Contábeis Aceitos¶
Filtro padrão para lançamentos (I200)¶
| Critério | Condição |
|---|---|
| Empresa | company_id = empresa |
| Período | date_ini ≤ date ≤ date_fim |
| Status | state = 'posted' |
| Valor | sum(line_ids.debit) > 0 — ignora lançamentos com valor total zero |
Classificação do lançamento¶
IND_LCTO |
Condição | Significado |
|---|---|---|
'N' |
is_encerramento = False |
Lançamento normal |
'E' |
is_encerramento = True |
Lançamento de encerramento do exercício |
Campo
is_encerramento: Boolean emaccount.move(linha 640 deaccount.py). Marcado automaticamente pelo módulo de Encerramento Contábil.
Serialização do Arquivo¶
Após gerar todos os registros, o método gerar_sped() serializa:
to_write["arquivo"] = str(uuid.uuid4())[:8] + "_%s_%s.TXT" % (date_ini, date_fim)
to_write["name"] = to_write["arquivo"][:-4]
to_write["situacao"] = "PROCESSADO"
to_write["arquivo_sped_ecd"] = base64.b64encode(_arq.getstring().encode('utf-8'))
| Saída | Formato | Exemplo |
|---|---|---|
| Nome do arquivo | {uuid8}_{DDMMAAAA}_{DDMMAAAA}.TXT |
a1b2c3d4_01012025_31122025.TXT |
| Protocolo | Idem sem .TXT |
a1b2c3d4_01012025_31122025 |
| Situação | Texto fixo | PROCESSADO |
| Arquivo | Base64 do TXT completo | Binário para download |
Dependências de Cadastro — Consolidado¶
| Entidade | Campo | Registros que usam | Obrigatório? |
|---|---|---|---|
res.company |
l10n_br_cnpj |
0000, I030 | Sim |
res.company |
name |
0000, I030, J900 | Sim |
res.company |
state_id.code |
0000, 0007 | Sim |
res.company |
l10n_br_ie |
0000, 0007 | Sim |
res.company |
l10n_br_municipio_id |
0000, I030 | Sim |
res.company |
l10n_br_im |
0000 | Não (fallback vazio) |
res.company |
l10n_br_nire |
I030 | Não (fallback vazio) |
res.company |
l10n_br_data_abertura |
I030 | Recomendado |
res.company |
l10n_br_contador_partner_id |
J930 | Sim (bloqueia) |
res.company |
l10n_br_sped_ecd_registros |
Todos | Sim (controle) |
account.account |
group_id |
I050, I155, I200 | Sim (bloqueia) |
account.account |
l10n_br_conta_referencial |
I051 | Sim* (exceto cod_nat 05/09) |
account.account |
l10n_br_cod_nat |
I050, I355, J100, J150 | Recomendado |
account.account |
code |
I050, I155, I250, I355 | Sim (Odoo nativo) |
account.group |
code_prefix_start |
I050, I052, J100, J150 | Sim (Odoo nativo) |
account.group |
l10n_br_cod_nat |
J100, J150 | Sim (filtro) |
account.group |
parent_id |
I050, J100, J150 | Recomendado (hierarquia) |
res.partner (contador) |
name, l10n_br_cpf, l10n_br_crc |
J930 | Sim |