Files
scoreodonto.com/documentação/BACKLOG-FINANCEIRO-V1.md
T
VPS 4 Builder 37cc818f4c docs: centraliza e reescreve a documentação em documentação/
- reúne todos os .md de ScoreOdonto numa pasta única (fonte única, dentro do repo versionado pelo Gitea)
- arquitetura.md: reescrito p/ o estado REAL (3 VPS, banco compartilhado dev/prod, sem staging/listener/registry fantasmas)
- deploy.md: reescrito p/ o estado-ALVO (Gitea CI/CD: push->build->registry->deploy por tag)
- DEPLOY-PRODUCAO.md: runbook operacional executável (fluxo manual real de hoje)
- VERDADE-HOJE.md: auditoria (codigo/banco/containers/deploy/versao/rollback)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 06:45:51 +02:00

19 KiB
Raw Blame History

Backlog V1 — Financeiro (ScoreOdonto)

Derivado do AUDITORIA-FUNCIONAL-SCOREODONTO.md (Deep-dive 1). Apenas planejamento — nada implementado. Padrões do projeto a seguir em toda história: tenantGuard + escopo clinica_id, soft delete onde fizer sentido, registrarAudit nas mutações, helpers buildInsert/buildUpdate, frontend useToast/useConfirm, rebuild da imagem DEV (frontend nginx + backend) — ver feedback_deploy_workflow. Estimativa: P (≤0,5 dia) · M (12 dias) · G (3+ dias).

Estado atual relevante: tabela financeiro(id, clinica_id, pacientenome[texto], descricao, valor, datavencimento, status[Pago/Pendente/Atrasado], formapagamento, tipo[RECEITA/DESPESA]), 0 linhas. Único elo: LancarProcedimentoModal → saveFinanceiro.


ÉPICO 1 — Ligar o Financeiro (FKs de origem) — IMPLEMENTADO (DEV)

Por quê: sem profissional_id/paciente_id não há comissão, repasse, nem relatório por paciente/profissional. É o pré-requisito de tudo.

Status (DEV): H1.1 (FKs + índices + soft delete + cron "Atrasado" — também cobre H2.2/H2.4) · H1.2 (FinanceiroView envia paciente_id) · H1.3 (LancarProcedimentoModal envia paciente_id/procedimento_id/origem='procedimento'). Extra entregue: prontuário de procedimentos realizados (procedimentos_realizados + fotos antes/depois) com regra de sem comissão em reabertura pelo mesmo profissional e em "refazer na garantia".

H1.1 — Adicionar chaves de ligação ao lançamento — M

  • Como sistema, quero que cada lançamento referencie paciente/profissional/origem para rastrear e relatar.
  • Migração: ALTER TABLE financeiro ADD COLUMN IF NOT EXISTS paciente_id TEXT, profissional_id TEXT, procedimento_id TEXT, agendamento_id TEXT, contrato_id TEXT, origem TEXT DEFAULT 'manual', pago_em TIMESTAMPTZ; + índices (clinica_id, status, datavencimento) e (clinica_id, profissional_id).
  • Backend: POST/PUT /api/financeiro aceitam os novos campos (já é buildInsert); manter clinica_id do tenantGuard.
  • Critérios: lançamento salva e retorna os campos; antigos (sem FK) continuam válidos; node --check + E2E (criar com profissional_id e ler).
  • Risco: pacientenome legado permanece (não migrar dado, base vazia em DEV).

H1.2 — Vincular paciente real (não só nome) no lançamento — M

  • Como recepção, quero selecionar o paciente cadastrado para o financeiro aparecer no histórico do paciente.
  • Frontend: no modal de novo lançamento (FinanceiroView), trocar campo de nome livre por busca de paciente (reusar getPacientes + componente do AppointmentModal); preencher paciente_id + pacientenome (snapshot).
  • Critérios: ao gravar, paciente_id preenchido; busca por nome/CPF/telefone funciona; manter compatível com lançamento sem paciente (despesa).

H1.3 — Procedimento concluído gera lançamento com FKs — M

  • Como sistema, quero que ao concluir um procedimento na agenda o lançamento já venha com paciente_id/profissional_id/procedimento_id/agendamento_id.
  • Backend/Frontend: LancarProcedimentoModal (que já chama saveFinanceiro) passa as FKs; valor sugerido = procedimentos.valorparticular.
  • Critérios: lançamento criado a partir de procedimento tem origem='procedimento' + as 4 FKs; valor pré-preenchido.

ÉPICO 2 — Contas a Receber / a Pagar

Por quê: o Financeiro hoje é uma lista plana; falta gestão de vencimentos, baixa e despesas estruturadas.

H2.1 — Baixa de pagamento com data e meio — P IMPLEMENTADO (DEV)

Modal BaixaPagamentoModal (data + forma) no FinanceiroView substitui o "marcar Pago" direto; useConfirm p/ valores ≥ R$ 1.000. PUT /api/financeiro/:id carimba pago_em (se ausente) e registra registrarAudit (baixa_pagamento, idempotente). Bug latente corrigido: enforceUppercase (service) mandava PAGO/PIX, violando os CHECK do banco (Pago/Pix); backend agora normaliza status/formapagamento/tipo no POST e no PUT (normalizeFinanceiro). Era a causa de a tabela financeiro ficar sempre vazia.

  • Como financeiro, quero confirmar pagamento registrando pago_em e a forma para ter caixa real.
  • Backend: PUT /api/financeiro/:id aceita status='Pago' + pago_em=NOW() (ou data informada). (PUT já é tenant-scoped.)
  • Frontend: botão "Confirmar pagamento" abre mini-form (data + forma) em vez de marcar direto; useConfirm para valores altos.
  • Critérios: ao confirmar, grava pago_em e formapagamento; KPI Recebido considera pago_em.

H2.2 — Job de "Atrasado" automático — P

  • Como sistema, quero marcar lançamentos vencidos como Atrasado para o KPI ser fiel.
  • Backend: cron (boot + 6h, padrão processarVigenciasContratos): UPDATE financeiro SET status='Atrasado' WHERE status='Pendente' AND datavencimento < CURRENT_DATE AND deleted_at IS NULL.
  • Pré-req: adicionar deleted_at ao financeiro (soft delete) — hoje DELETE é físico (H2.4).
  • Critérios: lançamento vencido vira Atrasado sem ação manual.

H2.3 — Despesas com fornecedor e centro de custo — M IMPLEMENTADO (DEV)

Migração fornecedor/centro_custo/recorrente. NovoLancamentoModal ganhou toggle Receita/Despesa: em DESPESA o paciente fica opcional e aparecem fornecedor + centro de custo (datalist com presets) + recorrente. Normalização de enums já cobre o casing. H3.3 entregue junto: porCategoria (despesas por centro de custo) no GET /api/financeiro/relatorio + bloco no ReportsView.

  • Como financeiro, quero classificar despesas (fornecedor, categoria/centro de custo, recorrente) para análise de saída.
  • Migração: ALTER TABLE financeiro ADD COLUMN IF NOT EXISTS fornecedor TEXT, centro_custo TEXT, recorrente BOOLEAN DEFAULT false;
  • Frontend: quando tipo=DESPESA, exibir fornecedor + centro de custo + recorrente.
  • Critérios: despesa salva com classificação; filtro por categoria no relatório (Épico 3).

H2.4 — Soft delete + auditoria do lançamento — P

  • Migração: ALTER TABLE financeiro ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
  • Backend: DELETE /api/financeiro/:idUPDATE ... SET deleted_at=NOW(); GET filtra deleted_at IS NULL; registrarAudit.
  • Critérios: excluir não apaga fisicamente; some das listas; audit registra autor.

ÉPICO 3 — Relatórios financeiros essenciais

Por quê: hoje só existe /api/reports/productivity.

Status (DEV): H3.1 + H3.2 — endpoint único GET /api/financeiro/relatorio?inicio&fim&pacienteId&profissionalId (tenantGuard, escopo clinica_id, soft delete) retornando resumo (recebido/a_receber/atrasado/despesa), porMes, porForma e lancamentos. UI em ReportsView (componente RelatorioFinanceiro): KPIs + barras por mês + por forma de pagamento + tabela + export CSV, com filtros de profissional (select) e paciente (busca). H3.3 (despesas por categoria/centro de custo) depende de H2.3 — pendente.

H3.1 — A receber/recebido por período — M

  • Backend: GET /api/financeiro/relatorio?inicio=&fim= (tenantGuard) → totais por status, por dia/mês, por forma de pagamento.
  • Frontend: seção em ReportsView (ou aba no Financeiro) com gráfico simples (reaproveitar padrão do Dashboard).
  • Critérios: filtros de período; soma confere com lançamentos.

H3.2 — Financeiro por paciente e por profissional — M

  • Backend: GET /api/financeiro/relatorio?pacienteId= e ?profissionalId= (usa as FKs do Épico 1).
  • Critérios: lista lançamentos do paciente/profissional no período; base para o repasse (V2).

H3.3 — Despesas por categoria/centro de custo — P IMPLEMENTADO (DEV, junto com H2.3)

  • Backend: agregação por centro_custo no relatório (porCategoria). ✓
  • Critérios: total por categoria no período. ✓ (bloco "Despesas por centro de custo" no ReportsView)

ÉPICO 4 — Orçamento → Contrato → Financeiro

Por quê: os 3 módulos existem isolados; ligá-los fecha o ciclo comercial.

Status (DEV): H4.1 — orçamento Assinado gera contas a receber automaticamente (entrada + N parcelas mensais a partir de data_inicio, origem='orcamento', FKs paciente/profissional/contrato_id), idempotente via orcamentos.financeiro_gerado; helper gerarFinanceiroOrcamento; toast no OrcamentoModal. H4.2 — contrato (motor) com cobrança recorrente: campos contratos.{cobranca_automatica,valor_recorrente,periodicidade(mensal|anual),dia_cobranca} + financeiro.competencia. Helper gerarCobrancaContratoCompetencia (idempotente por contrato_id+competência, origem='contrato', dia clamp 28) e gerarCobrancasRecorrentes rodando no cron processarVigenciasContratos (boot+6h) sobre contratos vigentes. Endpoint manual POST /api/contratos/instancias/:id/gerar-cobranca. UI: config no GeradorModal + botão "Gerar cobrança do mês" nos contratos vigentes (ContratosView). Renovação automática — no mesmo cron, contratos renovacao='automatica' com data_fim vencido estendem a vigência por nova duração igual à anterior (data_inicio=data_fim+1, data_fim += (data_fimdata_inicio)+1; fallback +12 meses sem início) e reabilitam o aviso de vencimento.

H4.1 — Orçamento aprovado gera contas a receber — M

  • Como recepção, quero que ao aprovar um orçamento as parcelas entrem no Financeiro para não relançar manualmente.
  • Backend: ao mudar status do orçamento para "Aprovado/Assinado", criar N lançamentos RECEITA (origem='orcamento', paciente_id, vencimentos por parcela) a partir de valor_total/entrada/parcelas.
  • Critérios: aprovar orçamento de 3x cria 3 lançamentos pendentes com vencimentos; idempotente (não duplica).

H4.2 — Contrato (motor) com cobrança recorrente — M (ponte p/ V2)

  • Como sistema, quero que um contrato vigente com vigência/parcelas gere lançamentos para cobrança recorrente.
  • Backend: estender o cron de vigência (processarVigenciasContratos) para emitir lançamentos conforme valores/periodicidade do contrato (contrato_id na FK).
  • Critérios: contrato mensal gera lançamento por competência; não duplica.

Sequência sugerida (entrega incremental)

  1. H1.1 → H2.4 → H2.2 (FKs + soft delete + atrasado) — base sólida e segura.
  2. H1.2 → H1.3 (paciente real + procedimento→lançamento) — liga clínica↔financeiro.
  3. H2.1 → H2.3 (baixa + despesas) — caixa fiel.
  4. H3.1 → H3.2 → H3.3 (relatórios) — visão gerencial.
  5. H4.1 → H4.2 (orçamento/contrato→financeiro) — fecha o ciclo e prepara o V2 (comissão/repasse).

Definição de pronto (toda história)

node --check (backend) + build OK (frontend) · endpoints com tenantGuard + escopo clinica_id · mutações com registrarAudit · E2E mínimo (criar/ler/escopo cross-tenant 403) · rebuild + recreate DEV · validado em dev.scoreodonto.com.

Riscos / observações

  • Não migrar dados legados (base de DEV vazia); em PROD, planejar backfill de paciente_id por pacientenome (fuzzy) — fora do V1.
  • formapagamento é texto livre hoje; padronizar enum (PIX/Cartão/Boleto/Dinheiro) facilita relatórios e o V2 (gateways).
  • Comissão/Repasse é V2 — depende do Épico 1 (FK profissional_id) entregue aqui.

V2 — Comissão / Repasse — IMPLEMENTADO (DEV)

Decisões: base selecionável (recebido=caixa via status Pago/pago_em · produção=competência via vencimento); percentual por procedimento com regra padrão de fallback (comissao_regras.procedimento_id NULL); fechamento persistido (snapshot imutável repasses + registrarAudit).

  • Tabelas: comissao_regras (clinica_id, procedimento_id NULL=padrão, percentual) (unique por clínica+proc); repasses (snapshot por profissional: período, base, base_valor, comissao_valor, detalhes JSONB, status, criado_por).
  • Cálculo (computeComissoes): RECEITA, sem_comissao=false, profissional_id NOT NULL; pct = regra do procedimento → senão padrão → senão 0; agrupa por profissional com detalhe por lançamento. Garantia/reabertura do mesmo profissional (sem_comissao) já saem de fora.
  • Endpoints: GET/PUT/DELETE /api/comissoes/regras, GET /api/comissoes/relatorio, POST /api/repasses/fechar, GET /api/repasses[/:id], POST /api/repasses/:id/cancelar.
  • Frontend: ComissoesView (menu COMISSÕES, owner-only) com abas Relatório/Fechamento (toggle recebido/produção, expandível por profissional, botão Fechar), Regras de % (padrão + por procedimento) e Histórico (lista de fechamentos + cancelar).
  • Gaps ( implementados DEV): (1) override de % por profissional (comissao_regras.profissional_id; prioridade prof > procedimento > padrão; unique inclui profissional); (2) custo de laboratório por procedimento (comissao_regras.custo_lab) deduzido do valor antes da comissão (base líquida); (3) marcar repasse como pago (POST /api/repasses/:id/pagar) que gera DESPESA no financeiro (origem='repasse', centro_custo='COMISSÕES', sem_comissao, Pago) e grava repasses.pago_em/financeiro_id; repasse pago não pode ser cancelado.
  • Estorno ao cancelar (): cancelar repasse pago agora soft-deleta a DESPESA gerada (financeiro.deleted_at) e zera repasses.financeiro_id; auditoria registra despesa_estornada. UI: botão cancelar disponível p/ status fechado e pago (confirm avisa do estorno).
  • GTO → Financeiro ( — gap descoberto): PUT /api/gto-builder/:id/finalizar agora gera RECEITA (convênio a receber, vencimento +30d, origem='gto', FKs paciente/profissional) — idempotente via gto_builders.financeiro_id. A tela LancarGTO não chamava os endpoints (handleFinalize era stub); agora persiste builder+itens+finaliza (service createGtoBuilder/addGtoItem/finalizarGto). Assim a produção por convênio entra no financeiro e na comissão (profissional=dentistaid).
  • Convênios padronizados (): GTO passou a usar convênios reais da tabela planos (menu PLANOS/CONVÊNIOS) via dropdown no LancarGTO (carrega getPlanos); credenciadaid agora guarda o id do plano (obrigatório). O lançamento GTO grava financeiro.convenio_id e a descrição traz o nome do convênio (GTO <id> — <nome>).
  • Relatório por convênio (): GET /api/financeiro/relatorio agora retorna porConvenio (RECEITA agrupada por convenio_id, join planos, com total/recebido/a_receber/qtd; sem convênio = "Particular"); bloco "Receitas por convênio" no ReportsView. Respeita os filtros de período/paciente/profissional.
  • Comprovante do repasse (): repasses.comprovante_{filename,mimetype,nome} + POST /api/repasses/:id/comprovante (multer memória, imagem/PDF, MEDIA_ROOT/repasses//) e GET /api/repasses/comprovante/:id/raw (URL assinada signMedia/verifyMedia); listagem retorna comprovante_url. UI no Histórico (ComissoesView): anexar (clip) / ver (PDF) nos repasses pagos.
  • Competência por conclusão (): financeiro.data_competencia (DATE) gravada na origem — procedimento realizado usa data_conclusao (campo "Concluído em" no prontuário; default hoje), GTO usa a data de finalização. computeComissoes base produção passou a usar COALESCE(data_competencia, datavencimento) (fallback p/ vencimento em lançamentos antigos). Recebido segue por pago_em.
  • Gaps remanescentes: glosa por item de GTO; recurso/protocolo de glosa com prazos.

GLOSAS DE CONVÊNIO — IMPLEMENTADO (DEV)

Por quê: convênio nega (glosa) parte do recebível; precisa marcar/gerenciar e recorrer.

  • Modelo: financeiro.valor_glosa (acumulado) + tabela glosas (financeiro_id, valor, motivo, status[glosado|recuperado], autor). Líquido a receber = valor valor_glosa.
  • Endpoints: GET /api/glosas[?status=], GET /api/glosas/recebiveis (RECEITA de convênio/GTO não totalmente glosada), POST /api/glosas (valida não exceder o restante; incrementa valor_glosa), POST /api/glosas/:id/recuperar (recurso ganho → devolve valor), DELETE /api/glosas/:id (correção). Todos tenantGuard + registrarAudit.
  • Frontend: GlosasView (menu GLOSAS, visível p/ donos e funcionário) — aba "Recebíveis/Marcar glosa" (busca + modal valor+motivo) e aba "Glosas" (filtro glosado/recuperado, recuperar/excluir).
  • Glosa no relatório (): GET /api/financeiro/relatorioresumo.glosa + resumo.a_receber_liquido (= a_receber+atrasado glosa) e coluna glosa em porConvenio; UI mostra KPI Glosa (com líquido) e coluna no relatório por convênio.
  • Glosa por item + recurso (): glosas.{gto_item_id,item_descricao,protocolo,prazo_recurso}; status agora glosado|recurso|recuperado. GET /api/glosas/itens/:financeiroId lista os gto_items da guia (via gto_builders.financeiro_id) com flag ja_glosado; POST aceita item/protocolo/prazo e bloqueia item já glosado (409). POST /api/glosas/:id/recorrer (protocolo+prazo, mantém valor_glosa); recuperar/excluir tratam status recurso. UI: seletor de item no GlosaModal + protocolo/prazo, filtro "Em recurso", ação Recorrer e exibição de item/protocolo/prazo.

Varredura final do módulo financeiro (DEV)

  • Isolamento multi-tenant: todos os endpoints novos (financeiro/relatorio, comissoes/, repasses/, glosas/*, contratos/.../gerar-cobranca) usam tenantGuard; teste cross-tenant retornou 403 em todos (exceto os */raw de mídia, protegidos por URL assinada). Mutações por :id validam clinica_id antes de alterar.
  • Índices adicionados: idx_financeiro_contrato_comp (contrato_id,competencia), idx_financeiro_convenio, idx_glosas_financeiro, idx_glosas_item, idx_gto_builders_financeiro (além dos já existentes idx_financeiro_clinica/_prof, idx_glosas_clinica, idx_repasses_clinica, uq_comissao_regra2).
  • Limitações conhecidas (futuro): (1) glosa não reduz automaticamente a base de comissão "recebido" se um recebível glosado for marcado Pago com valor cheio — ao dar baixa em recebível de convênio com glosa, reduzir o valor pago; (2) orçamento financeiro_gerado=true evita duplicar parcelas, mas não regenera se o valor mudar após assinado (falta ação "regenerar").

Contrato para dentista que atende convênios ()

Novo modelo global cmod_odo_14"Atendimento a Convênios / Repasse (Dentista Credenciado)" (tipo dentista-clinica), com cláusulas de: objeto (convênios atendidos), guias/GTO e faturamento, repasse (% sobre produção, base recebido/produção, prazo), custo de laboratório, glosas (responsável + dedução do repasse + registro de protocolo/prazo), obrigações, ausência de vínculo, vigência/rescisão, LGPD e foro. Novas variáveis em VARIAVEIS_CONTRATO: {{CONVENIOS}}, {{PERCENTUAL_REPASSE}}, {{BASE_REPASSE}}, {{PRAZO_REPASSE}}, {{RESPONSAVEL_GLOSA}}, {{CUSTO_LABORATORIO}} — aparecem automaticamente no Gerador de contrato (data-driven). Liga-se ao financeiro: repasse (base recebido/produção), custo de lab e glosas implementados no módulo.