Speed / src /fiscal /entities.py
LesterCerioli's picture
Building Speed LLM
d9d7b41
Raw
History Blame Contribute Delete
7.38 kB
from __future__ import annotations
import re
from datetime import date
from decimal import Decimal
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field, field_validator
class RegimeTributario(str, Enum):
LUCRO_REAL = "1"
LUCRO_PRESUMIDO = "2"
SIMPLES_NACIONAL = "3"
ARBITRADO = "4"
IMUNE = "5"
OPTANTE_SIMPLES = "6"
LUCRO_REAL_TRIMESTRAL = "7"
class TipoEmpresa(str, Enum):
INDUSTRIA = "industria"
COMERCIO = "comercio"
SERVICOS = "servicos"
MISTO = "misto"
class PerfilSPED(str, Enum):
A = "A" # Escrituração completa
B = "B" # Escrituração com dados simplificados
C = "C" # Escrituração com dados simplificados (ME/EPP Simples)
class UF(str, Enum):
AC = "AC"; AL = "AL"; AP = "AP"; AM = "AM"
BA = "BA"; CE = "CE"; DF = "DF"; ES = "ES"
GO = "GO"; MA = "MA"; MT = "MT"; MS = "MS"
MG = "MG"; PA = "PA"; PB = "PB"; PR = "PR"
PE = "PE"; PI = "PI"; RJ = "RJ"; RN = "RN"
RS = "RS"; RO = "RO"; RR = "RR"; SC = "SC"
SP = "SP"; SE = "SE"; TO = "TO"
def _limpar_cnpj(cnpj: str) -> str:
return re.sub(r"\D", "", cnpj)
def _validar_cnpj(cnpj: str) -> bool:
d = _limpar_cnpj(cnpj)
if len(d) != 14 or d == d[0] * 14:
return False
for i, j in [(12, 13), (13, 14)]:
s = sum(int(d[k]) * ((i - k - 1) % 8 + 2) for k in range(i))
c = 0 if (r := s % 11) < 2 else 11 - r
if c != int(d[j - 1]):
return False
return True
def _validar_cpf(cpf: str) -> bool:
d = re.sub(r"\D", "", cpf)
if len(d) != 11 or d == d[0] * 11:
return False
for i in range(9, 11):
s = sum(int(d[k]) * (i + 1 - k) for k in range(i))
if (0 if (r := (s * 10 % 11)) >= 10 else r) != int(d[i]):
return False
return True
class Endereco(BaseModel):
logradouro: str
numero: str
complemento: str = ""
bairro: str
municipio: str
uf: UF
cep: str
cod_municipio: str = ""
@field_validator("cep")
@classmethod
def normalizar_cep(cls, v: str) -> str:
return re.sub(r"\D", "", v).zfill(8)
class Contato(BaseModel):
nome: str = ""
telefone: str = ""
fax: str = ""
email: str = ""
class Empresa(BaseModel):
cnpj: str
razao_social: str
nome_fantasia: str = ""
ie: str = ""
im: str = ""
suframa: str = ""
regime_tributario: RegimeTributario
tipo_empresa: TipoEmpresa
endereco: Endereco
contato: Contato = Field(default_factory=Contato)
perfil_sped: PerfilSPED = PerfilSPED.A
atividade_industrial: bool = False
data_inicio_atividade: Optional[date] = None
@field_validator("cnpj")
@classmethod
def validar_cnpj(cls, v: str) -> str:
d = _limpar_cnpj(v)
if not _validar_cnpj(d):
raise ValueError(f"CNPJ inválido: {v}")
return d
@property
def cnpj_formatado(self) -> str:
d = self.cnpj
return f"{d[:2]}.{d[2:5]}.{d[5:8]}/{d[8:12]}-{d[12:]}"
class Contador(BaseModel):
nome: str
cpf: str
crc: str
cnpj_escritorio: str = ""
endereco: Optional[Endereco] = None
contato: Contato = Field(default_factory=Contato)
@field_validator("cpf")
@classmethod
def validar_cpf(cls, v: str) -> str:
d = re.sub(r"\D", "", v)
if not _validar_cpf(d):
raise ValueError(f"CPF inválido: {v}")
return d
class Produto(BaseModel):
codigo: str
descricao: str
ncm: str
ex_ipi: str = ""
unidade: str
tipo_item: str = "00"
cod_gen: str = ""
aliq_icms: Decimal = Decimal("0")
aliq_ipi: Decimal = Decimal("0")
cst_icms: str = "000"
cst_ipi: str = "50"
cst_pis: str = "01"
cst_cofins: str = "01"
cest: str = ""
cod_barras: str = ""
peso_bruto: Decimal = Decimal("0")
peso_liquido: Decimal = Decimal("0")
class ItemNotaFiscal(BaseModel):
numero_item: int
produto: Produto
cfop: str
quantidade: Decimal
valor_unitario: Decimal
desconto: Decimal = Decimal("0")
outras_despesas: Decimal = Decimal("0")
frete: Decimal = Decimal("0")
seguro: Decimal = Decimal("0")
@property
def valor_produtos(self) -> Decimal:
return (self.quantidade * self.valor_unitario) - self.desconto
@property
def base_icms(self) -> Decimal:
return self.valor_produtos + self.frete + self.seguro + self.outras_despesas
@property
def valor_icms(self) -> Decimal:
return (self.base_icms * self.produto.aliq_icms / 100).quantize(Decimal("0.01"))
@property
def base_ipi(self) -> Decimal:
return self.valor_produtos
@property
def valor_ipi(self) -> Decimal:
return (self.base_ipi * self.produto.aliq_ipi / 100).quantize(Decimal("0.01"))
@property
def valor_total(self) -> Decimal:
return self.valor_produtos + self.valor_ipi + self.frete + self.seguro + self.outras_despesas
class NotaFiscal(BaseModel):
numero: str
serie: str
modelo: str = "55"
data_emissao: date
data_saida_entrada: date
hora_saida_entrada: str = "00:00:00"
emitente: Empresa
destinatario: Empresa
natureza_operacao: str
tipo_operacao: str = "1"
ind_pagamento: str = "0"
chave_acesso: str = ""
protocolo: str = ""
itens: list[ItemNotaFiscal] = Field(default_factory=list)
info_complementar: str = ""
@property
def valor_produtos(self) -> Decimal:
return sum(i.valor_produtos for i in self.itens)
@property
def valor_frete(self) -> Decimal:
return sum(i.frete for i in self.itens)
@property
def valor_seguro(self) -> Decimal:
return sum(i.seguro for i in self.itens)
@property
def valor_desconto(self) -> Decimal:
return sum(i.desconto for i in self.itens)
@property
def valor_outras_despesas(self) -> Decimal:
return sum(i.outras_despesas for i in self.itens)
@property
def valor_icms(self) -> Decimal:
return sum(i.valor_icms for i in self.itens)
@property
def valor_ipi(self) -> Decimal:
return sum(i.valor_ipi for i in self.itens)
@property
def valor_total(self) -> Decimal:
return sum(i.valor_total for i in self.itens)
class LancamentoContabil(BaseModel):
data: date
numero_lancamento: str
historico: str
debito_conta: str
credito_conta: str
valor: Decimal
complemento: str = ""
class MovimentacaoEstoque(BaseModel):
data: date
produto: Produto
tipo_movimento: str
quantidade: Decimal
valor_unitario: Decimal
documento: str = ""
@property
def valor_total(self) -> Decimal:
return self.quantidade * self.valor_unitario
class PeriodoApuracao(BaseModel):
data_inicio: date
data_fim: date
empresa: Empresa
notas_saida: list[NotaFiscal] = Field(default_factory=list)
notas_entrada: list[NotaFiscal] = Field(default_factory=list)
lancamentos: list[LancamentoContabil] = Field(default_factory=list)
movimentacoes: list[MovimentacaoEstoque] = Field(default_factory=list)
@property
def total_vendas(self) -> Decimal:
return sum(nf.valor_total for nf in self.notas_saida)
@property
def total_compras(self) -> Decimal:
return sum(nf.valor_total for nf in self.notas_entrada)