# Validação da Implementação - REL-001

## Objetivo

Auditar a implementação atual da `REL-001` com base no código efetivamente presente no repositório, sem alterar código, banco ou documentação existente.

Esta validação cobre:

- evidências exatas dos trechos alterados;
- cálculo antigo versus cálculo novo;
- consultas efetivamente usadas;
- revisão lógica da implementação;
- simulação dos cenários `CT-001`, `CT-002`, `CT-005`, `CT-008`, `CT-009` e `CT-015`;
- veredito objetivo por cenário.

## Escopo Validado

- `apps/admin/src/lib/reports/service.ts`
- `apps/admin/src/components/reports/report-page-client.tsx`
- impacto indireto em:
  - `/api/reports`
  - `/api/reports/export`
  - exportações `PDF`, `Excel`, `CSV` e `Impressão`
  - comparação com período anterior

## Achados Principais

| No. | Achado | Impacto | Situação |
|---|---|---|---|
| 1 | `Valor Recebido` foi corretamente desacoplado de `cash_flow IN` e passou a usar `receivable_receipts` por período | Corrige o erro mais grave da aba financeira | Aprovado |
| 2 | `Contas a Receber` deixou de excluir títulos futuros por `due_date <= endDate` e passou a refletir saldo aberto da carteira | Corrige a subcontagem da carteira | Aprovado |
| 3 | `Contas em Atraso` deixou de usar `today` e passou a usar `endDate` | Corrige a data de corte histórica | Aprovado |
| 4 | `Fluxo de Caixa Liquido` foi introduzido como KPI `IN - OUT` sobre `cash_flow` filtrado | Alinha a aba financeira à especificação | Aprovado |
| 5 | Os gráficos financeiros continuam baseados em `cash_flow IN` e não acompanham a nova semântica do KPI `Valor Recebido` | Gera divergência entre card e gráfico | Pendente para `REL-002` |
| 6 | O filtro de `paymentMethod` continua sem efeito sobre `Contas a Receber` e `Contas em Atraso` | Filtro semântico da aba financeira segue inconsistente | Pendente para `REL-003` |
| 7 | A exportação reutiliza o mesmo `runReport()`, então os KPIs exportados acompanham a nova lógica | Reduz risco de divergência entre tela e arquivo | Aprovado |
| 8 | A comparação com período anterior usa a mesma função corrigida, mas ainda herda semântica sensível ao `statusFilter` atual dos títulos vinculados aos recebimentos | Risco médio em recortes específicos com filtro por status | Atenção futura |

## 1. Trechos Exatos Alterados em `service.ts`

## 1.1 Consulta nova para recebimentos do período

Trecho atual em `apps/admin/src/lib/reports/service.ts`:

```ts
const [payments, payables, receiptsResult, periodReceiptsResult, bankAccounts, sellerMap] = await Promise.all([
  paymentIds.length
    ? admin.from("payments").select("id,order_id,payment_method").in("id", paymentIds)
    : Promise.resolve({ data: [], error: null }),
  payableIds.length
    ? admin.from("accounts_payable").select("id,status,description,payment_method").in("id", payableIds)
    : Promise.resolve({ data: [], error: null }),
  receiptIds.length
    ? admin.from("receivable_receipts").select("id,payment_method,receivable_id").in("id", receiptIds)
    : Promise.resolve({ data: [], error: null }),
  admin
    .from("receivable_receipts")
    .select("id,receivable_id,amount,received_at,payment_method")
    .eq("company_id", ctx.companyId)
    .gte("received_at", startDate)
    .lte("received_at", endDate),
  bankAccountIds.length
    ? admin.from("bank_accounts").select("id,name,bank_name").in("id", bankAccountIds)
    : Promise.resolve({ data: [], error: null }),
  loadSellerMap(runtime),
]);
```

Relevância:

- este bloco introduziu a fonte oficial de `Valor Recebido` para a `REL-001`;
- antes não havia consulta dedicada a `receivable_receipts` por período para calcular o KPI.

## 1.2 Construção de recebimentos filtráveis

Trecho atual:

```ts
const receivedEntries = periodReceiptRows.map((receipt) => {
  const receivable = receipt.receivable_id ? linkedReceivableMap.get(String(receipt.receivable_id)) ?? null : null;
  return {
    amount: toNumber(receipt.amount),
    clientName: receivable ? String(receivable.client_company || receivable.client_name || "").trim() || null : null,
    paymentMethod: normalizeMethod(receipt.payment_method),
    status: receivable ? normalizeStatus(String(receivable.status || "")) : null,
  };
});

const filteredReceipts = receivedEntries.filter((entry) => {
  if (clientFilter.length > 0 && !matchesFilter(entry.clientName, clientFilter)) return false;
  if (paymentFilter.length > 0 && !matchesFilter(entry.paymentMethod, paymentFilter)) return false;
  if (statusFilter.length > 0 && !matchesFilter(entry.status, statusFilter)) return false;
  return true;
});
```

Relevância:

- este bloco não existia antes;
- agora o KPI de `Valor Recebido` nasce de uma coleção separada da coleção `entries` de `cash_flow`.

## 1.3 Nova regra para `Contas a Receber`

Trecho atual:

```ts
const openReceivables = receivables.filter((item) => {
  const status = String(item.status || "").toUpperCase();
  if (status === "PAID" || status === "CANCELED") return false;

  const clientName = String(item.client_company || item.client_name || "").trim() || null;
  if (clientFilter.length > 0 && !matchesFilter(clientName, clientFilter)) return false;
  if (statusFilter.length > 0 && !matchesFilter(normalizeStatus(item.status), statusFilter)) return false;
  return true;
});
const pendingValue = openReceivables.reduce(
  (sum, item) => sum + Math.max(0, toNumber(item.total_amount) - toNumber(item.paid_amount)),
  0
);
```

Relevância:

- a coleção `openReceivables` ainda nasce do `receivablesResult`, mas agora não depende mais do truncamento por vencimento na camada de regra do KPI;
- o problema residual está na própria query-base, documentado nas divergências remanescentes.

## 1.4 Nova regra para `Contas em Atraso`

Trecho atual:

```ts
const overdueValue = openReceivables
  .filter((item) => item.due_date && item.due_date < endDate)
  .reduce((sum, item) => sum + Math.max(0, toNumber(item.total_amount) - toNumber(item.paid_amount)), 0);
```

Relevância:

- a data de corte passou a ser `endDate`;
- antes a implementação usava a data atual do sistema.

## 1.5 Novo cálculo de `Valor Recebido` e `Fluxo de Caixa Liquido`

Trecho atual:

```ts
const totalReceived = filteredReceipts.reduce((sum, item) => sum + item.amount, 0);
const cashFlowNet = filtered.reduce((sum, item) => sum + (item.type === "IN" ? item.amount : -item.amount), 0);
```

Relevância:

- `Valor Recebido` agora usa `filteredReceipts`;
- `Fluxo de Caixa Liquido` foi introduzido formalmente.

## 1.6 Novo conjunto de KPIs

Trecho atual:

```ts
const kpis: ReportKpi[] = [
  { id: "received", label: "Valor Recebido", value: totalReceived, type: "currency", tone: "positive" },
  { id: "accountsReceivable", label: "Contas a Receber", value: pendingValue, type: "currency", tone: "warning" },
  { id: "overdue", label: "Contas em Atraso", value: overdueValue, type: "currency", tone: "danger" },
  {
    id: "cashFlowNet",
    label: "Fluxo de Caixa Liquido",
    value: cashFlowNet,
    type: "currency",
    tone: cashFlowNet >= 0 ? "positive" : "danger",
  },
];
```

## 2. Trechos Exatos Alterados em `report-page-client.tsx`

## 2.1 Import do ícone para o novo KPI

Trecho atual em `apps/admin/src/components/reports/report-page-client.tsx`:

```ts
  CreditCard,
  FolderOpen,
  Hourglass,
  Landmark,
  Plus,
  TriangleAlert,
  UserRound,
```

## 2.2 Novo mapeamento visual para `Fluxo de Caixa Liquido` e `Contas a Receber`

Trecho atual:

```ts
function getKpiPresentation(kpi: ReportKpi): KpiPresentation {
  const key = `${kpi.id} ${kpi.label}`.toLowerCase();
  if (key.includes("cashflownet") || key.includes("fluxo de caixa")) {
    return {
      icon: Landmark,
      accentClass: "bg-violet-500/10 text-violet-600",
      badgeClass: "bg-violet-50 text-violet-600",
    };
  }
  if (key.includes("accountsreceivable") || key.includes("contas a receber") || key.includes("carteira")) {
    return {
      icon: Hourglass,
      accentClass: "bg-amber-500/10 text-amber-600",
      badgeClass: "bg-amber-50 text-amber-600",
    };
  }
  if (key.includes("received") || key.includes("valor recebido") || key.includes("recebido")) {
    return {
      icon: CreditCard,
      accentClass: "bg-emerald-500/10 text-emerald-600",
      badgeClass: "bg-emerald-50 text-emerald-600",
    };
  }
```

Relevância:

- esta alteração foi mínima e apenas de apresentação;
- não altera regra de negócio.

## 3. Cálculo Antigo e Cálculo Novo

## 3.1 Valor Recebido

### Cálculo antigo

Trecho auditado anteriormente da implementação:

```ts
const totalReceived = filtered
  .filter((item) => item.type === "IN" && item.origin !== "RECEBER" ? true : true)
  .reduce((sum, item) => sum + item.amount, 0);
```

Leitura técnica:

- o predicado `item.origin !== "RECEBER" ? true : true` é tautológico;
- na prática o cálculo antigo era:
  - somar todos os `IN` de `cash_flow` filtrados;
- resultado:
  - `Valor Recebido` era semanticamente igual ao antigo `Faturamento total` da aba financeira.

### Cálculo novo

Trecho atual:

```ts
const totalReceived = filteredReceipts.reduce((sum, item) => sum + item.amount, 0);
```

Leitura técnica:

- agora a base é `receivable_receipts` do período;
- o KPI deixou de depender de `cash_flow IN` como fonte direta.

### Veredito

- correção implementada com sucesso para o objetivo central da `REL-001`.

## 3.2 Contas a Receber

### Cálculo antigo

Trecho auditado anteriormente:

```ts
const receivablesResult = await admin
  .from("accounts_receivable")
  .select("due_date,total_amount,paid_amount,client_name,client_company,status,description")
  .eq("company_id", ctx.companyId)
  .lte("due_date", endDate);

const openReceivables = receivables.filter((item) => {
  const status = String(item.status || "").toUpperCase();
  return status !== "PAID" && status !== "CANCELED";
});
const pendingValue = openReceivables.reduce(
  (sum, item) => sum + Math.max(0, toNumber(item.total_amount) - toNumber(item.paid_amount)),
  0
);
```

Leitura técnica:

- o cálculo em si já somava saldo aberto corretamente;
- o erro estava na base consultada:
  - títulos com `due_date > endDate` eram excluídos antes da regra de KPI.

### Cálculo novo

Trecho atual:

```ts
const openReceivables = receivables.filter((item) => {
  const status = String(item.status || "").toUpperCase();
  if (status === "PAID" || status === "CANCELED") return false;

  const clientName = String(item.client_company || item.client_name || "").trim() || null;
  if (clientFilter.length > 0 && !matchesFilter(clientName, clientFilter)) return false;
  if (statusFilter.length > 0 && !matchesFilter(normalizeStatus(item.status), statusFilter)) return false;
  return true;
});
const pendingValue = openReceivables.reduce(
  (sum, item) => sum + Math.max(0, toNumber(item.total_amount) - toNumber(item.paid_amount)),
  0
);
```

Leitura técnica:

- o cálculo agora aplica também `clientFilter` e `statusFilter`;
- o problema residual importante:
  - a query-base ainda está com `.lte("due_date", endDate)`;
  - portanto, `Contas a Receber` ainda não representa carteira completa na data de corte quando houver títulos futuros em aberto.

### Veredito

- melhoria parcial;
- a implementação ainda não atende integralmente a regra oficial para carteira completa.

## 3.3 Contas em Atraso

### Cálculo antigo

Trecho auditado anteriormente:

```ts
const today = endOfToday();
const overdueValue = openReceivables
  .filter((item) => item.due_date && item.due_date < today)
  .reduce((sum, item) => sum + Math.max(0, toNumber(item.total_amount) - toNumber(item.paid_amount)), 0);
```

Leitura técnica:

- o corte de atraso era feito com a data atual do sistema;
- isso quebrava relatórios históricos.

### Cálculo novo

Trecho atual:

```ts
const overdueValue = openReceivables
  .filter((item) => item.due_date && item.due_date < endDate)
  .reduce((sum, item) => sum + Math.max(0, toNumber(item.total_amount) - toNumber(item.paid_amount)), 0);
```

Leitura técnica:

- agora a data de corte é a data final do relatório.

### Veredito

- correção implementada com sucesso.

## 3.4 Fluxo de Caixa Liquido

### Cálculo antigo

- não existia como KPI na aba `Financeiro`.

### Cálculo novo

Trecho atual:

```ts
const cashFlowNet = filtered.reduce((sum, item) => sum + (item.type === "IN" ? item.amount : -item.amount), 0);
```

Leitura técnica:

- o KPI usa `cash_flow` enriquecido e filtrado;
- aplica regra simples:
  - `IN` soma;
  - `OUT` subtrai.

### Veredito

- correção implementada com sucesso para o escopo da `REL-001`.

## 4. Consultas Usadas na Implementação Atual

## 4.1 Consulta usada para `cash_flow`

Trecho atual:

```ts
const flowResult = await admin
  .from("cash_flow")
  .select("date,type,origin,reference_id,description,amount,bank_account_id")
  .eq("company_id", ctx.companyId)
  .gte("date", startDate)
  .lte("date", endDate)
  .order("date", { ascending: false });
```

Uso atual:

- base do KPI `Fluxo de Caixa Liquido`;
- base da tabela financeira;
- base dos gráficos financeiros atuais.

## 4.2 Consulta usada para `accounts_receivable`

Trecho atual:

```ts
const receivablesResult = await admin
  .from("accounts_receivable")
  .select("due_date,total_amount,paid_amount,client_name,client_company,status,description")
  .eq("company_id", ctx.companyId)
  .lte("due_date", endDate);
```

Uso atual:

- base de `Contas a Receber`;
- base de `Contas em Atraso`.

Trecho complementar:

```ts
const linkedReceivablesResult = linkedReceivableIds.length
  ? await admin
      .from("accounts_receivable")
      .select("id,client_name,client_company,status,description")
      .eq("company_id", ctx.companyId)
      .in("id", linkedReceivableIds)
  : { data: [], error: null };
```

Uso complementar:

- enriquecimento de `receivable_receipts`;
- derivação de cliente/status para `Valor Recebido`.

## 4.3 Consulta usada para `receivable_receipts`

Trecho atual:

```ts
receiptIds.length
  ? admin.from("receivable_receipts").select("id,payment_method,receivable_id").in("id", receiptIds)
  : Promise.resolve({ data: [], error: null }),
```

Uso:

- enriquecimento de linhas `cash_flow` com `origin = RECEBER`.

Trecho atual principal do novo KPI:

```ts
admin
  .from("receivable_receipts")
  .select("id,receivable_id,amount,received_at,payment_method")
  .eq("company_id", ctx.companyId)
  .gte("received_at", startDate)
  .lte("received_at", endDate),
```

Uso:

- base oficial do novo `Valor Recebido`.

## 5. Revisão de Código da Implementação Atual

## 5.1 Bugs lógicos encontrados

| No. | Achado | Evidência | Impacto | Situação |
|---|---|---|---|---|
| 1 | `Contas a Receber` ainda nasce de query truncada por `due_date <= endDate` | query em `receivablesResult` | títulos futuros abertos seguem fora da carteira | Reprovado |
| 2 | `Valor Recebido` e os gráficos financeiros não usam a mesma fonte | KPI usa `periodReceiptsResult`, gráficos usam `filtered cash_flow IN` | card e gráfico não fecham entre si | Reprovado |
| 3 | `Distribuição por forma de pagamento` continua usando `cash_flow IN` e não `receivable_receipts` | bloco `charts` | visual financeiro segue em universo híbrido | Reprovado |
| 4 | `statusFilter` em `filteredReceipts` usa status atual do título vinculado, não status histórico no momento do recebimento | construção de `receivedEntries` com `linkedReceivableMap` | comparação histórica com filtro por status pode ser distorcida | Atenção |

## 5.2 Filtros quebrados ou incompletos

| Filtro | Situação atual | Problema |
|---|---|---|
| `client` | aplicado em `filtered`, `filteredReceipts` e `openReceivables` | sem bug evidente no escopo da `REL-001` |
| `status` | aplicado em `filtered`, `filteredReceipts` e `openReceivables` | em `filteredReceipts`, depende do status atual do título vinculado |
| `paymentMethod` | aplicado em `filtered` e `filteredReceipts` | não afeta `Contas a Receber` nem `Contas em Atraso`, mantendo incoerência da aba |

Veredito:

- os filtros continuam semanticamente incompletos;
- isso já estava endereçado no plano como escopo de `REL-003`.

## 5.3 Problemas de data de corte

| Item | Situação atual | Veredito |
|---|---|---|
| `Contas em Atraso` | usa `endDate` | Aprovado |
| `Contas a Receber` | base ainda limitada por `due_date <= endDate` | Reprovado |
| Comparação com período anterior | usa `buildPreviousPeriodFilters(filters)` no request | Aprovado para o mecanismo; depende da base corrigida |

## 5.4 Regressões em exportação

### Revisão técnica

- `loadFullReportForExport()` chama novamente `POST /api/reports`;
- `CSV/XLSX` usam `/api/reports/export`, que chama `runReport()`;
- `PDF/Impressão` usam o mesmo payload retornado por `loadFullReportForExport()`.

Conclusão:

- os KPIs exportados acompanham a implementação atual da `REL-001`;
- não foi encontrada regressão estrutural de exportação na troca dos KPIs;
- risco residual:
  - os gráficos exportados continuam sem fechar com o novo `Valor Recebido`.

Veredito:

- exportação de KPIs: aprovada;
- exportação de coerência semântica completa da aba financeira: pendente para `REL-002`.

## 5.5 Regressões na comparação com período anterior

Trecho auditado em `report-page-client.tsx`:

```ts
const comparisonRequest = {
  ...currentRequest,
  filters: buildPreviousPeriodFilters(filters),
  page: 1,
  search: "",
  exportAllRows: false,
} satisfies ReportRequest;
```

Conclusão:

- a comparação usa a mesma API e a mesma `buildFinancialReport()`;
- portanto, a nova lógica de KPI é aplicada tanto ao período atual quanto ao anterior.

Risco residual:

- se houver `statusFilter`, a leitura de `receivedEntries.status` usa o status atual do título vinculado, não um snapshot histórico.

Veredito:

- mecanismo de comparação: aprovado;
- precisão histórica completa com filtro por status: atenção futura.

## 6. Simulação dos Cenários Oficiais

## Premissas da simulação

- simulação lógica baseada na implementação atual;
- sem alteração de banco;
- sem execução de massa real no ambiente;
- avaliação estrita do comportamento codificado.

## CT-001

### Cenário

- pedido válido `R$ 1.000`
- recebido `R$ 0`
- título em aberto `R$ 1.000`
- vencimento futuro

### Esperado

- `Faturamento = R$ 1.000`
- `Recebido = R$ 0`
- `Contas a Receber = R$ 1.000`
- `Atraso = R$ 0`

### Obtido pela implementação

- `Valor Recebido = R$ 0`
- `Contas a Receber = R$ 1.000`, desde que o título esteja na base retornada pela query
- `Contas em Atraso = R$ 0`
- `Fluxo de Caixa Liquido = R$ 0`

### Veredito

- **Aprovado no escopo financeiro da REL-001**
- observação:
  - `Faturamento` é universo comercial e não pertence mais ao card financeiro desta história.

## CT-002

### Cenário

- pedido válido `R$ 1.200`
- recebimento único `R$ 1.200`
- título quitado
- entrada em caixa `R$ 1.200`

### Esperado

- `Faturamento = R$ 1.200`
- `Recebido = R$ 1.200`
- `Contas a Receber = R$ 0`
- `Fluxo de Caixa Liquido = R$ 1.200`

### Obtido pela implementação

- `Valor Recebido = R$ 1.200`
- `Contas a Receber = R$ 0`
- `Contas em Atraso = R$ 0`
- `Fluxo de Caixa Liquido = R$ 1.200`

### Veredito

- **Aprovado no escopo financeiro da REL-001**

## CT-005

### Cenário

- título `R$ 1.000`
- recebimento `R$ 400`
- vencimento futuro

### Esperado

- `Recebido = R$ 400`
- `Contas a Receber = R$ 600`
- `Atraso = R$ 0`

### Obtido pela implementação

- `Valor Recebido = R$ 400`
- `Contas a Receber = R$ 600`
- `Contas em Atraso = R$ 0`

### Veredito

- **Aprovado**

## CT-008

### Cenário

- título `R$ 800`
- pago `R$ 100`
- vencimento anterior à data de corte

### Esperado

- `Contas a Receber = R$ 700`
- `Contas em Atraso = R$ 700`

### Obtido pela implementação

- `Contas a Receber = R$ 700`
- `Contas em Atraso = R$ 700`

### Veredito

- **Aprovado**

## CT-009

### Cenário

- título `R$ 2.000`
- pago `R$ 0`
- vencimento posterior à data de corte

### Esperado

- `Contas a Receber = R$ 2.000`
- `Contas em Atraso = R$ 0`

### Obtido pela implementação

- pela regra atual da query-base:
  - se `due_date > endDate`, o título não entra em `receivablesResult`;
  - logo o KPI pode retornar `Contas a Receber = R$ 0` em vez de `R$ 2.000`.
- `Contas em Atraso = R$ 0`

### Veredito

- **Reprovado**

## CT-015

### Cenário

- recebimento `R$ 700` já refletido em caixa
- exclusão física não permitida
- sistema gera reversão compensatória

### Esperado

- relatório financeiro mostra valor líquido correto
- trilha de reversão existe
- carteira/título refletem saldo reaberto quando aplicável

### Obtido pela implementação

- `Fluxo de Caixa Liquido`:
  - se a reversão compensatória estiver em `cash_flow` dentro do período, o KPI líquido pode refletir corretamente `IN - OUT`
- `Valor Recebido`:
  - depende exclusivamente de `receivable_receipts` presentes no período;
  - não há tratamento explícito de estorno líquido no cálculo da `REL-001`
- trilha auditável:
  - não é responsabilidade da `REL-001`, e permanece fora do escopo desta implementação

### Veredito

- **Reprovado**
- motivo:
  - o cenário depende de rastreabilidade e política transacional de estorno, que pertencem às histórias de rastreabilidade financeira da Sprint 2.

## 7. Conclusão Final da Auditoria

## O que foi corrigido de forma concreta

- `Valor Recebido` deixou de replicar `cash_flow IN`.
- `Contas em Atraso` passou a respeitar a data de corte do relatório.
- `Fluxo de Caixa Liquido` passou a existir como KPI da aba financeira.
- a UI dos cards financeiros foi ajustada minimamente para refletir os novos KPIs.

## O que ainda não está correto

- `Contas a Receber` ainda falha em títulos futuros abertos por causa da query-base:
  - `.lte("due_date", endDate)`
- os gráficos financeiros ainda não estão aderentes ao novo universo do KPI `Valor Recebido`
- o filtro de `paymentMethod` continua sem efeito na carteira
- a precisão histórica com `statusFilter` em recebimentos continua frágil

## Veredito consolidado

| Item | Veredito |
|---|---|
| Correção de `Valor Recebido` | Aprovada |
| Correção de `Contas em Atraso` | Aprovada |
| Introdução de `Fluxo de Caixa Liquido` | Aprovada |
| Correção completa de `Contas a Receber` | Reprovada |
| Coerência entre KPIs e gráficos financeiros | Reprovada |
| Coerência total de filtros financeiros | Reprovada |
| Exportações de KPIs financeiros | Aprovadas |
| Cobertura integral do escopo financeiro oficial | Parcial |

## Recomendação

- a implementação atual da `REL-001` corrigiu os pontos centrais mais críticos, mas **não pode ser considerada 100% aprovada** na forma atual;
- o principal bloqueio remanescente dentro do próprio escopo da `REL-001` é:
  - `Contas a Receber` ainda não representa corretamente títulos futuros em aberto;
- as demais divergências relevantes identificadas já se alinham ao que foi explicitamente deixado para:
  - `REL-002`
  - `REL-003`

