Pular para conteúdo

Mapeamento

As regras abaixo são o contrato de parsing de cada um dos 9 tipos: como o ArquivoDetector decide o tipo, de qual aba/coluna cada campo sai, quais linhas são descartadas e em que ordem as tabelas são populadas. Nomes lógicos (MOVIMENTO, CLIENTE…) correspondem às tabelas físicas bi_movimento, bi_cliente, etc. (ver modelagem).

1. Detecção de tipo de arquivo

Ordem de verificação (a primeira que satisfazer vence):

  1. Aba "Meta por vendedor" existe (ou coluna "Tipo de meta") → META_FERNANDO
  2. 1ª linha da aba padrão tem coluna "Vinculação a Distribuidor" (com/sem acento) → POSTOS
  3. 1ª linha da aba padrão tem coluna "Tipo de Instalação" ou "Qualificação da Empresa" (com/sem acento) → TRR
  4. Nome (lower-case) contém relatorio / relatório / rel 18 / relatorio18 → RELATORIO18
  5. Nome (lower-case) contém margem consolidada ou margem_consolidada → MARGEM_CONSOLIDADA
  6. Nome (lower-case) contém margem - (espaço + hífen) → MARGEM_DIA
  7. Nome (lower-case) contém meta → META
  8. Nenhuma condição → desconhecido → BadRequestException → HTTP 400 (não persiste)

Arquivos temporários (nome começa com ~$ ou #) → 400 antes de qualquer análise.

Bug do Python corrigido no Java

O Python fazia 'margem consolidada' or 'margem_consolidada' in nome — sempre True (string não-vazia é truthy). O Java usa a lógica correta: nome.contains("margem consolidada") || nome.contains("margem_consolidada").

2. Extração da data de referência

Tipo Origem da data Exemplo Resultado
RELATORIO18 yyyyMMdd no fim do nome relatorio18_20240316.xlsx 2024-03-16
MARGEM_CONSOLIDADA MM.YYYY no nome margem_consolidada_03.2024.xlsx 2024-03-01 (dia 1)
MARGEM_DIA DD.MM.YYYY no nome margem - 15.03.2024.xlsx 2024-03-15
POSTOS / TRR — (data de processamento) qualquer LocalDate.now().minusDays(1) (ontem)
META / META_FERNANDO coluna DATA no arquivo qualquer do Excel

3. Aba lida por tipo

Tipo Aba Observação
POSTOS / TRR padrão (índice 0) detecção pela coluna de cabeçalho
RELATORIO18 padrão (índice 0) dados de NF; aba Inventário processada à parte (§6)
MARGEM_CONSOLIDADA DADOS obrigatória — não usar índice 0
MARGEM_DIA DADOS obrigatória — não usar índice 0
META padrão (índice 0) —
META_FERNANDO Meta por vendedor (ou aba com "Tipo de meta") prioridade: aba nomeada

4. Mapeamento de colunas Excel → banco

4.1 POSTOS → bi_posto

Coluna Excel Campo Regra
Razão Social razao_social strip
CNPJ cnpj strip; chave natural
MUNICÍPIO municipio strip
UF uf strip
Vinculação a Distribuidor distribuidor strip
(injetado) data LocalDate.now().minusDays(1)

Upsert por cnpj; linha com qualquer campo nulo é removida.

4.2 TRR → bi_trr

Coluna Excel Campo Regra
Razão Social razao_social strip
CNPJ cnpj strip; chave natural; dedup keep-last
Município municipio strip
UF uf strip
(injetado) data LocalDate.now().minusDays(1)

4.3 RELATORIO18 → bi_filial, bi_cliente, bi_vendedor, bi_produto, bi_movimento

Coluna Cód duplicada

O cabeçalho tem Cód duas vezes. O pandas renomeava para Cód.1; o POI SAX não renomeia — mapear por posição: 1ª ocorrência = Cód (cliente), 2ª ocorrência = Códp (produto).

bi_filial (upsert por cod_filial): FILIAL→cod_filial, Município→municipio, UF→uf. bi_cliente (upsert por cod_cliente): Cód (1ª)→cod_cliente, R.Social→razao_social, CNPJ→cnpj, Município→municipio, UF→uf, Class→classe. bi_vendedor (upsert por cod_vendedor): Cod.Vendedor→cod_vendedor, Vendedor→nome. bi_produto (upsert por cod_produto): Cód (2ª = Códp)→cod_produto, Produto→nome.

bi_movimento (upsert por nf, cod_produto, cod_cliente, cod_filial):

Coluna Excel Campo Regra especial
NF nf numérico; descartar se inválido
DATA NF data DATE; descartar linha se nulo
FILIAL cod_filial TEXT
UF / Município uf / municipio TEXT
CIF cif CHAR(1)
Qtde. quantidade DECIMAL; descartar se inválido
PV preco DECIMAL; descartar se inválido
ADM taxa_adm DECIMAL; descartar se inválido
FRETE frete DECIMAL; descartar se inválido
Margem Total margem DECIMAL
PRAZO prazo INTEGER; nulo/vazio → 0
Bandeira / Categoria bandeira / categoria TEXT (nullable)
Cód (1ª) cod_cliente FK bi_cliente
Cod.Vendedor cod_vendedor FK bi_vendedor
Cód (2ª=Códp) cod_produto FK bi_produto

Validação: descartar linha se DATA NF nula ou se qualquer de NF, Qtde., PV, ADM, FRETE não for numérico (logar cada descarte: NF, razão social, CNPJ, produto, linha). Ordenar por NF antes do upsert.

4.4 MARGEM_CONSOLIDADA → mesmas 5 tabelas

FILIAL aqui é o nome da cidade

A coluna Excel FILIAL contém o nome do município (não o código). O código está em CÓD FILIAL.

bi_filial: CÓD FILIAL→cod_filial, FILIAL→municipio (nome da cidade), UF→uf. bi_cliente: Cód→cod_cliente, R.Social→razao_social, CNPJ→cnpj, Município→municipio, UF→uf. bi_vendedor: RESP→cod_vendedor, VENDEDORES→nome. bi_produto: Códp→cod_produto, Produto→nome. bi_movimento (sem prazo/bandeira/categoria): NF→nf, DATA→data, CÓD FILIAL→cod_filial, UF→uf, Município→municipio, CIF→cif, Qtde.→quantidade, PV→preco, ADM→taxa_adm, FRETE→frete, Margem Total→margem, Cód→cod_cliente, RESP→cod_vendedor, Códp→cod_produto.

4.5 MARGEM_DIA → como MARGEM_CONSOLIDADA, exceto

  • FILIAL → cod_filial (aqui a coluna FILIAL contém o código, não a cidade); Município→municipio.
  • bi_movimento.data é injetado do nome do arquivo (não há coluna DATA no Excel).
  • Vendedor: VENDEDORES→nome (igual à consolidada).

4.6 META → bi_meta

CODIGO_VENDEDOR→cod_vendedor (strip), DATA→data, LITROS→litros (DECIMAL), MARGEM→margem (DECIMAL). Upsert por (cod_vendedor, data): DO UPDATE SET litros = EXCLUDED.litros, margem = EXCLUDED.margem.

4.7 META_FERNANDO → bi_meta

Vendedor→cod_vendedor (INTEGER cast p/ TEXT), Data→data (parse dd/MM/yyyy), Valor da meta (L)→volume (DECIMAL; descartar se nulo).

Transformações:

  1. Agrupar por (cod_vendedor, mês) somando volume → litros.
  2. data = 1º dia do mês (LocalDate.of(ano, mes, 1)).
  3. Arredondar litros se próximo de inteiro redondo: ordem = nº de dígitos de floor(valor); tolerancia = 0.1 * 10^(ordem-1); se |valor − round(valor)| < tolerancia → round(valor). Ex.: 3.999.999,99 → 4.000.000.

Upsert por (cod_vendedor, data): DO UPDATE SET litros = EXCLUDED.litros (não atualiza margem).

5. Limpeza de bi_movimento antes do insert

RELATORIO18 — DELETE por período (adotado no Java)

DELETE FROM bi_movimento WHERE data >= :periodo_inicio AND data <= :periodo_fim

periodo_inicio = MIN(DATA NF), periodo_fim = MAX(DATA NF) do arquivo.

Por que por-período e não open-ended

O Python usava DELETE WHERE data >= min_date (aberto para frente) — só correto se os arquivos chegam em ordem cronológica. Na API REST a ordem de upload é não-determinística: subir Fev/2025 e depois Jan/2025 (correção) apagaria fevereiro. O DELETE por período preserva os demais meses.

Data mínima: se periodo_inicio ≤ 10/08/2025 → status IGNORADO (não é ERRO; log de aviso; nenhuma alteração no banco). A data-limite existe porque registros anteriores foram migrados/corrigidos à mão — reprocessá-los sobrescreveria a correção. A mesma guarda de data mínima vale para MARGEM_CONSOLIDADA e MARGEM_DIA.

MARGEM_CONSOLIDADA / MARGEM_DIA — sem DELETE prévio

Só upsert. Registros ausentes no novo arquivo permanecem (os arquivos de margem representam a visão completa do período; reupload sem mudança é no-op).

6. Aba Inventário (opcional em RELATORIO18 e MARGEM_CONSOLIDADA) → bi_custo_inventario

Se presente: DATA→data, VALOR→valor, TAXA_APLICACAO→taxa_aplicacao, TOTAL_JUROS→total_juros, TOTAL_VENDA_SIMULADA→total_venda_simulada, TOTAL→total. Upsert por data. Aba ausente → ignorar silenciosamente (sem erro).

7. Filtro de movimentos (antigo hack do Power BI) — não implementar no Java

O script Python 08-retirar-movimento-indesejado.py copiava registros com cod_filial = '10' e cod_produto IN ('7','8') para uma tabela de excluídos e os removia de MOVIMENTO. No Java esses registros permanecem em bi_movimento; toda query de BI deve aplicar WHERE NOT (cod_filial = '10' AND cod_produto IN ('7','8')). Documentado no Javadoc de Relatorio18Processor.

8. Deduplicação dentro do arquivo

Tipo Chave de dedup (keep-last)
POSTOS / TRR cnpj
RELATORIO18 / MARGEM_CONSOLIDADA / MARGEM_DIA (nf, cod_produto, cod_cliente, cod_filial)
META sem dedup interna (o upsert resolve)
META_FERNANDO (cod_vendedor, mês) via agrupamento/soma

9. Ordem de upsert (FK-safe)

Para os tipos que populam múltiplas tabelas, a ordem é obrigatória (garante as FKs de bi_movimento):

bi_filial → bi_cliente → bi_vendedor → bi_produto → bi_movimento
  • RELATORIO18 / MARGEM_CONSOLIDADA / MARGEM_DIA populam as 5 tabelas.
  • META / META_FERNANDO populam só bi_meta (o cod_vendedor já deve existir).
  • POSTOS → só bi_posto; TRR → só bi_trr.

10. Validação na recepção (síncrona, antes de enfileirar)

  1. Magic bytes ZIP 0x50 0x4B 0x03 0x04 (xlsx) — senão 400.
  2. Nome não começa com ~$ nem # — senão 400.
  3. Detectar o tipo (conteúdo/nome) — desconhecido → 400.
  4. Calcular checksum SHA-256 e conferir deduplicação de upload.
  5. Persistir bytes + nome + checksum + tipo_arquivo + status PENDENTE.
  6. Enfileirar o processamento assíncrono.

11. Status do processamento

Status Quando
PENDENTE recebido e validado; aguardando a thread
PROCESSANDO thread iniciou; banco sendo alterado
SUCESSO todos os upserts concluídos sem exceção
ERRO exceção durante o processamento (erro_mensagem preenchido)
IGNORADO tipo detectado mas período ≤ 10/08/2025 (RELATORIO18/MARGEM_); não* é falha

Recepção: 202 → PENDENTE; 200 → arquivo com checksum já existente em SUCESSO; 400 → arquivo inválido ou tipo desconhecido (não persiste).