ajustado filtros na dashboard e movimentos

This commit is contained in:
Diego Freitas 2026-06-08 22:30:31 -03:00
parent 180d756195
commit 12dcc8506f
6 changed files with 1924 additions and 391 deletions

View File

@ -157,87 +157,225 @@ async function buscarCardsMovimentos(idUsuario, dataInicio, dataFim) {
const [rows] = await pool.query( const [rows] = await pool.query(
` `
SELECT SELECT
COALESCE(SUM(CASE COALESCE(SUM(
WHEN cp.movimento IN ('Entrada', 'Estorno') THEN cp.valor CASE
ELSE 0 /* Entradas e estornos normais */
END), 0) AS entradas, WHEN cp.movimento IN ('Entrada', 'Estorno')
AND banco_origem.idusuarios = ?
THEN cp.valor
COALESCE(SUM(CASE /* Sangria recebida de outro usuário */
WHEN cp.movimento IN ('Saida', 'Sangria') WHEN cp.movimento = 'Sangria'
AND banco_destino.idusuarios = ?
AND (
banco_origem.idusuarios IS NULL
OR banco_origem.idusuarios <> ?
)
THEN cp.valor
ELSE 0
END
), 0) AS entradas,
COALESCE(SUM(
CASE
/* Saída operacional normal */
WHEN cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 0 AND COALESCE(cc.investimento, 0) = 0
THEN cp.valor THEN cp.valor
ELSE 0
END), 0) AS saidas,
COALESCE(SUM(CASE /* Sangria enviada para outro usuário */
WHEN cp.movimento IN ('Saida', 'Sangria') WHEN cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND (
banco_destino.idusuarios IS NULL
OR banco_destino.idusuarios <> ?
)
THEN cp.valor
ELSE 0
END
), 0) AS saidas,
COALESCE(SUM(
CASE
/* Saída marcada como investimento pelo centro de custo */
WHEN cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 1 AND COALESCE(cc.investimento, 0) = 1
THEN cp.valor THEN cp.valor
ELSE 0
END), 0) AS investimentos,
COALESCE(SUM(CASE /* Transferência para banco de investimento do próprio usuário */
WHEN cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND banco_destino.idusuarios = ?
AND COALESCE(banco_destino.investimento, 0) = 1
THEN cp.valor
ELSE 0
END
), 0) AS investimentos,
COALESCE(SUM(
CASE
WHEN cp.status = 'A receber' WHEN cp.status = 'A receber'
AND cp.movimento IN ('Entrada', 'Estorno') AND cp.movimento IN ('Entrada', 'Estorno')
AND banco_origem.idusuarios = ?
THEN cp.valor THEN cp.valor
ELSE 0 ELSE 0
END), 0) AS abertoEntradas, END
), 0) AS abertoEntradas,
COUNT(CASE COUNT(
CASE
WHEN cp.status = 'A receber' WHEN cp.status = 'A receber'
AND cp.movimento IN ('Entrada', 'Estorno') AND cp.movimento IN ('Entrada', 'Estorno')
AND banco_origem.idusuarios = ?
THEN 1 THEN 1
ELSE NULL ELSE NULL
END) AS quantidadeAbertoEntradas, END
) AS quantidadeAbertoEntradas,
COALESCE(SUM(CASE COALESCE(SUM(
CASE
WHEN cp.status = 'A pagar' WHEN cp.status = 'A pagar'
AND cp.movimento IN ('Saida', 'Sangria') AND cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 0 AND COALESCE(cc.investimento, 0) = 0
THEN cp.valor THEN cp.valor
ELSE 0 ELSE 0
END), 0) AS abertoSaidas, END
), 0) AS abertoSaidas,
COUNT(CASE COUNT(
CASE
WHEN cp.status = 'A pagar' WHEN cp.status = 'A pagar'
AND cp.movimento IN ('Saida', 'Sangria') AND cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 0 AND COALESCE(cc.investimento, 0) = 0
THEN 1 THEN 1
ELSE NULL ELSE NULL
END) AS quantidadeAbertoSaidas, END
) AS quantidadeAbertoSaidas,
COALESCE(SUM(CASE COALESCE(SUM(
CASE
WHEN cp.status = 'A pagar' WHEN cp.status = 'A pagar'
AND cp.movimento IN ('Saida', 'Sangria') AND cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 1 AND COALESCE(cc.investimento, 0) = 1
THEN cp.valor THEN cp.valor
ELSE 0 ELSE 0
END), 0) AS abertoInvestimentos, END
), 0) AS abertoInvestimentos,
COUNT(CASE COUNT(
CASE
WHEN cp.status = 'A pagar' WHEN cp.status = 'A pagar'
AND cp.movimento IN ('Saida', 'Sangria') AND cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 1 AND COALESCE(cc.investimento, 0) = 1
THEN 1 THEN 1
ELSE NULL ELSE NULL
END) AS quantidadeAbertoInvestimentos, END
) AS quantidadeAbertoInvestimentos,
COALESCE(SUM(CASE COALESCE(SUM(
WHEN cp.status IN ('Pago', 'Recebido') THEN cp.valor CASE
WHEN cp.status IN ('Pago', 'Recebido')
AND (
cp.movimento <> 'Sangria'
OR (
cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND (
banco_destino.idusuarios IS NULL
OR banco_destino.idusuarios <> ?
)
)
OR (
cp.movimento = 'Sangria'
AND banco_destino.idusuarios = ?
AND banco_origem.idusuarios <> ?
)
)
THEN cp.valor
ELSE 0 ELSE 0
END), 0) AS baixado, END
), 0) AS baixado,
COUNT(*) AS quantidadeMovimentos COUNT(*) AS quantidadeMovimentos
FROM contasapagar cp FROM contasapagar cp
INNER JOIN bancos b
ON b.idbancos = cp.idbancos INNER JOIN bancos banco_origem
ON banco_origem.idbancos = cp.idbancos
LEFT JOIN bancos banco_destino
ON banco_destino.idbancos = cp.idbancos_p
LEFT JOIN centrodecustos cc LEFT JOIN centrodecustos cc
ON cc.idcentrodecustos = cp.idcentrodecustos ON cc.idcentrodecustos = cp.idcentrodecustos
WHERE cp.deleted_at IS NULL WHERE cp.deleted_at IS NULL
AND b.idusuarios = ? AND (
AND DATE(cp.datavencimento) BETWEEN ? AND ? banco_origem.idusuarios = ?
OR banco_destino.idusuarios = ?
)
AND DATE(COALESCE(cp.dataentrada, cp.insert_date))
BETWEEN ? AND ?
`, `,
[idUsuario, dataInicio, dataFim] [
/* entradas */
idUsuario,
idUsuario,
idUsuario,
/* saídas */
idUsuario,
idUsuario,
idUsuario,
/* investimentos */
idUsuario,
idUsuario,
idUsuario,
/* abertoEntradas */
idUsuario,
/* quantidadeAbertoEntradas */
idUsuario,
/* abertoSaidas */
idUsuario,
/* quantidadeAbertoSaidas */
idUsuario,
/* abertoInvestimentos */
idUsuario,
/* quantidadeAbertoInvestimentos */
idUsuario,
/* baixado */
idUsuario,
idUsuario,
idUsuario,
idUsuario,
/* usuário na origem ou destino */
idUsuario,
idUsuario,
/* período */
dataInicio,
dataFim,
]
); );
const row = rows[0] || {}; const row = rows[0] || {};
@ -267,9 +405,14 @@ async function buscarCardsMovimentos(idUsuario, dataInicio, dataFim) {
abertoInvestimentos, abertoInvestimentos,
aberto: abertoEntradas - abertoSaidas - abertoInvestimentos, aberto: abertoEntradas - abertoSaidas - abertoInvestimentos,
quantidadeAbertoEntradas: normalizarNumero(row.quantidadeAbertoEntradas), quantidadeAbertoEntradas:
quantidadeAbertoSaidas: normalizarNumero(row.quantidadeAbertoSaidas), normalizarNumero(row.quantidadeAbertoEntradas),
quantidadeAbertoInvestimentos: normalizarNumero(row.quantidadeAbertoInvestimentos),
quantidadeAbertoSaidas:
normalizarNumero(row.quantidadeAbertoSaidas),
quantidadeAbertoInvestimentos:
normalizarNumero(row.quantidadeAbertoInvestimentos),
baixado: normalizarNumero(row.baixado), baixado: normalizarNumero(row.baixado),
quantidadeMovimentos: normalizarNumero(row.quantidadeMovimentos), quantidadeMovimentos: normalizarNumero(row.quantidadeMovimentos),
@ -430,7 +573,8 @@ async function buscarAlertasVencimento(idUsuario) {
} }
async function buscarProximosVencimentos(idUsuario) { async function buscarProximosVencimentos(idUsuario) {
const [rows] = await pool.query(` const [rows] = await pool.query(
`
SELECT SELECT
cp.idcontasapagar, cp.idcontasapagar,
cp.movimento, cp.movimento,
@ -443,22 +587,40 @@ async function buscarProximosVencimentos(idUsuario) {
cp.parcela, cp.parcela,
cp.parcelas, cp.parcelas,
cp.idbancos, cp.idbancos,
bc.descricao AS banco_descricao,
bc.debito AS banco_debito, banco_origem.descricao AS banco_descricao,
banco_origem.debito AS banco_debito,
cp.idcentrodecustos, cp.idcentrodecustos,
cc.descricao AS centro_custo_descricao, cc.descricao AS centro_custo_descricao,
DATEDIFF(DATE(cp.datavencimento), CURDATE()) AS dias_para_vencer
DATEDIFF(
DATE(cp.datavencimento),
CURDATE()
) AS dias_para_vencer
FROM contasapagar cp FROM contasapagar cp
INNER JOIN bancos bc ON bc.idbancos = cp.idbancos
LEFT JOIN centrodecustos cc ON cc.idcentrodecustos = cp.idcentrodecustos INNER JOIN bancos banco_origem
ON banco_origem.idbancos = cp.idbancos
LEFT JOIN centrodecustos cc
ON cc.idcentrodecustos = cp.idcentrodecustos
WHERE cp.deleted_at IS NULL WHERE cp.deleted_at IS NULL
AND bc.idusuarios = ? AND banco_origem.idusuarios = ?
AND cp.status IN ('A pagar', 'A receber') AND cp.status IN ('A pagar', 'A receber')
AND cp.movimento IN ('Entrada', 'Estorno', 'Saida')
AND cp.datavencimento IS NOT NULL AND cp.datavencimento IS NOT NULL
ORDER BY DATE(cp.datavencimento) ASC, cp.idcontasapagar ASC
ORDER BY
DATE(cp.datavencimento) ASC,
cp.idcontasapagar ASC
LIMIT 10 LIMIT 10
`, `,
[idUsuario]); [idUsuario]
);
return rows; return rows;
} }
@ -626,7 +788,8 @@ async function buscarCarteiras(idUsuario, dataInicio, dataFim) {
} }
async function buscarUltimosMovimentos(idUsuario) { async function buscarUltimosMovimentos(idUsuario) {
const [rows] = await pool.query(` const [rows] = await pool.query(
`
SELECT SELECT
cp.idcontasapagar, cp.idcontasapagar,
cp.movimento, cp.movimento,
@ -643,87 +806,362 @@ async function buscarUltimosMovimentos(idUsuario) {
cp.saldo_processado, cp.saldo_processado,
cp.insert_date, cp.insert_date,
cp.update_date, cp.update_date,
cp.idbancos, cp.idbancos,
bc.descricao AS banco_descricao, cp.idbancos_p,
bc.debito AS banco_debito,
banco_origem.descricao AS banco_descricao,
banco_origem.descricao AS banco_origem_descricao,
banco_origem.debito AS banco_debito,
banco_origem.idusuarios AS idusuario_origem,
usuario_origem.nome AS usuario_origem_nome,
banco_destino.descricao AS banco_destino_descricao,
banco_destino.debito AS banco_destino_debito,
banco_destino.idusuarios AS idusuario_destino,
usuario_destino.nome AS usuario_destino_nome,
cp.idcentrodecustos, cp.idcentrodecustos,
cc.descricao AS centro_custo_descricao cc.descricao AS centro_custo_descricao,
CASE
/*
* Movimento normal:
* mantém o tipo original.
*/
WHEN cp.movimento <> 'Sangria'
THEN cp.movimento
/*
* Origem e destino pertencem ao usuário:
* transferência interna.
*/
WHEN banco_origem.idusuarios = ?
AND banco_destino.idusuarios = ?
THEN 'TransferenciaInterna'
/*
* Usuário é o dono do banco de origem:
* dinheiro saiu para outro usuário.
*/
WHEN banco_origem.idusuarios = ?
THEN 'Saida'
/*
* Usuário é o dono do banco de destino:
* dinheiro entrou vindo de outro usuário.
*/
WHEN banco_destino.idusuarios = ?
THEN 'Entrada'
ELSE cp.movimento
END AS movimento_dashboard,
CASE
WHEN cp.movimento <> 'Sangria'
AND cp.movimento IN ('Entrada', 'Estorno')
THEN 'entrada'
WHEN cp.movimento <> 'Sangria'
AND cp.movimento = 'Saida'
THEN 'saida'
WHEN cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND banco_destino.idusuarios = ?
THEN 'transferencia_interna'
WHEN cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
THEN 'saida'
WHEN cp.movimento = 'Sangria'
AND banco_destino.idusuarios = ?
THEN 'entrada'
ELSE 'neutro'
END AS direcao_dashboard,
CASE
WHEN cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND banco_destino.idusuarios = ?
THEN 1
ELSE 0
END AS transferencia_interna
FROM contasapagar cp FROM contasapagar cp
INNER JOIN bancos bc ON bc.idbancos = cp.idbancos
LEFT JOIN centrodecustos cc ON cc.idcentrodecustos = cp.idcentrodecustos INNER JOIN bancos banco_origem
ON banco_origem.idbancos = cp.idbancos
LEFT JOIN bancos banco_destino
ON banco_destino.idbancos = cp.idbancos_p
LEFT JOIN usuarios usuario_origem
ON usuario_origem.idusuarios = banco_origem.idusuarios
LEFT JOIN usuarios usuario_destino
ON usuario_destino.idusuarios = banco_destino.idusuarios
LEFT JOIN centrodecustos cc
ON cc.idcentrodecustos = cp.idcentrodecustos
WHERE cp.deleted_at IS NULL WHERE cp.deleted_at IS NULL
AND bc.idusuarios = ? AND (
ORDER BY cp.insert_date DESC, cp.idcontasapagar DESC banco_origem.idusuarios = ?
OR banco_destino.idusuarios = ?
)
ORDER BY
cp.insert_date DESC,
cp.idcontasapagar DESC
LIMIT 10 LIMIT 10
`, `,
[idUsuario]); [
/* movimento_dashboard */
idUsuario,
idUsuario,
idUsuario,
idUsuario,
return rows; /* direcao_dashboard */
idUsuario,
idUsuario,
idUsuario,
idUsuario,
/* transferencia_interna */
idUsuario,
idUsuario,
/* WHERE */
idUsuario,
idUsuario,
]
);
return rows.map((item) => ({
...item,
valor: normalizarNumero(item.valor),
transferencia_interna:
Number(item.transferencia_interna || 0) === 1,
}));
} }
async function buscarGraficos(idUsuario, dataInicio, dataFim) { async function buscarGraficos(idUsuario, dataInicio, dataFim) {
/*
* Gastos por centro de custo
*
* Aqui entram:
* - Saídas normais do usuário;
* - Sangrias enviadas para outro usuário;
*
* Não entram:
* - Sangrias entre bancos do próprio usuário;
* - Sangrias recebidas;
* - Movimentos classificados como investimento.
*/
const [centrosRows] = await pool.query( const [centrosRows] = await pool.query(
` `
SELECT SELECT
cp.idcentrodecustos, cp.idcentrodecustos,
COALESCE(cc.descricao, 'Sem centro de custo') AS descricao, COALESCE(cc.descricao, 'Sem centro de custo') AS descricao,
COALESCE(SUM(cp.valor), 0) AS valor COALESCE(SUM(cp.valor), 0) AS valor
FROM contasapagar cp FROM contasapagar cp
INNER JOIN bancos banco_origem
ON banco_origem.idbancos = cp.idbancos
LEFT JOIN bancos banco_destino
ON banco_destino.idbancos = cp.idbancos_p
LEFT JOIN centrodecustos cc LEFT JOIN centrodecustos cc
ON cc.idcentrodecustos = cp.idcentrodecustos ON cc.idcentrodecustos = cp.idcentrodecustos
LEFT JOIN bancos b
ON b.idbancos = cp.idbancos
WHERE cp.deleted_at IS NULL WHERE cp.deleted_at IS NULL
AND cp.movimento IN ('Saida', 'Sangria') AND DATE(COALESCE(cp.dataentrada, cp.insert_date))
BETWEEN ? AND ?
AND COALESCE(cc.investimento, 0) = 0 AND COALESCE(cc.investimento, 0) = 0
AND DATE(cp.datavencimento) BETWEEN ? AND ?
AND b.idusuarios = ? AND (
(
cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
)
OR
(
cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND (
banco_destino.idusuarios IS NULL
OR banco_destino.idusuarios <> ?
)
)
)
GROUP BY GROUP BY
cp.idcentrodecustos, cp.idcentrodecustos,
COALESCE(cc.descricao, 'Sem centro de custo') COALESCE(cc.descricao, 'Sem centro de custo')
HAVING valor > 0 HAVING valor > 0
ORDER BY valor DESC ORDER BY valor DESC
LIMIT 8 LIMIT 8
`, `,
[dataInicio, dataFim, idUsuario] [
dataInicio,
dataFim,
idUsuario,
idUsuario,
idUsuario,
]
); );
/*
* Resumo de entradas, saídas e investimentos.
*
* Para sangria:
* - destino é do usuário e origem é de outro usuário = entrada;
* - origem é do usuário e destino é de outro usuário = saída;
* - origem e destino são do usuário = transferência interna, ignorada.
*/
const [entradasSaidasRows] = await pool.query( const [entradasSaidasRows] = await pool.query(
` `
SELECT SELECT
COALESCE(SUM(CASE COALESCE(
WHEN cp.movimento IN ('Entrada', 'Estorno') THEN cp.valor SUM(
ELSE 0 CASE
END), 0) AS entradas, /* Entrada ou estorno normal */
WHEN cp.movimento IN ('Entrada', 'Estorno')
AND banco_origem.idusuarios = ?
THEN cp.valor
COALESCE(SUM(CASE /* Sangria recebida de outro usuário */
WHEN cp.movimento IN ('Saida', 'Sangria') WHEN cp.movimento = 'Sangria'
AND banco_destino.idusuarios = ?
AND banco_origem.idusuarios <> ?
THEN cp.valor
ELSE 0
END
),
0
) AS entradas,
COALESCE(
SUM(
CASE
/* Saída normal */
WHEN cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 0 AND COALESCE(cc.investimento, 0) = 0
THEN cp.valor THEN cp.valor
ELSE 0
END), 0) AS saidas,
COALESCE(SUM(CASE /* Sangria enviada para outro usuário */
WHEN cp.movimento IN ('Saida', 'Sangria') WHEN cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND (
banco_destino.idusuarios IS NULL
OR banco_destino.idusuarios <> ?
)
AND COALESCE(cc.investimento, 0) = 0
THEN cp.valor
ELSE 0
END
),
0
) AS saidas,
COALESCE(
SUM(
CASE
WHEN cp.movimento = 'Saida'
AND banco_origem.idusuarios = ?
AND COALESCE(cc.investimento, 0) = 1 AND COALESCE(cc.investimento, 0) = 1
THEN cp.valor THEN cp.valor
WHEN cp.movimento = 'Sangria'
AND banco_origem.idusuarios = ?
AND banco_destino.idusuarios = ?
AND COALESCE(banco_destino.investimento, 0) = 1
THEN cp.valor
ELSE 0 ELSE 0
END), 0) AS investimentos END
),
0
) AS investimentos
FROM contasapagar cp FROM contasapagar cp
INNER JOIN bancos banco_origem
ON banco_origem.idbancos = cp.idbancos
LEFT JOIN bancos banco_destino
ON banco_destino.idbancos = cp.idbancos_p
LEFT JOIN centrodecustos cc LEFT JOIN centrodecustos cc
ON cc.idcentrodecustos = cp.idcentrodecustos ON cc.idcentrodecustos = cp.idcentrodecustos
LEFT JOIN bancos b
ON b.idbancos = cp.idbancos
WHERE cp.deleted_at IS NULL WHERE cp.deleted_at IS NULL
AND DATE(cp.datavencimento) BETWEEN ? AND ? AND DATE(COALESCE(cp.dataentrada, cp.insert_date))
AND b.idusuarios = ? BETWEEN ? AND ?
AND (
banco_origem.idusuarios = ?
OR banco_destino.idusuarios = ?
)
`, `,
[dataInicio, dataFim, idUsuario] [
/* Entradas */
idUsuario,
idUsuario,
idUsuario,
/* Saídas */
idUsuario,
idUsuario,
idUsuario,
/* Investimentos */
idUsuario,
idUsuario,
idUsuario,
/* Período */
dataInicio,
dataFim,
/* Usuário presente em uma das pontas */
idUsuario,
idUsuario,
]
);
const entradas = normalizarNumero(
entradasSaidasRows[0]?.entradas
);
const saidas = normalizarNumero(
entradasSaidasRows[0]?.saidas
);
const investimentos = normalizarNumero(
entradasSaidasRows[0]?.investimentos
); );
const entradas = normalizarNumero(entradasSaidasRows[0]?.entradas);
const saidas = normalizarNumero(entradasSaidasRows[0]?.saidas);
const investimentos = normalizarNumero(entradasSaidasRows[0]?.investimentos);
const resultado = entradas - saidas - investimentos; const resultado = entradas - saidas - investimentos;
return { return {
@ -741,8 +1179,14 @@ async function buscarGraficos(idUsuario, dataInicio, dataFim) {
percentualEntradas: entradas > 0 ? 100 : 0, percentualEntradas: entradas > 0 ? 100 : 0,
percentualSaidas: calcularPercentual(saidas, entradas), percentualSaidas: calcularPercentual(saidas, entradas),
percentualInvestimentos: calcularPercentual(investimentos, entradas), percentualInvestimentos: calcularPercentual(
percentualResultado: calcularPercentual(resultado, entradas), investimentos,
entradas
),
percentualResultado: calcularPercentual(
resultado,
entradas
),
}, },
}; };
} }

View File

@ -72,7 +72,14 @@ function formatarData(data: string | null | undefined) {
} }
function movimentoColor(movimento: string) { function movimentoColor(movimento: string) {
if (movimento === 'Entrada' || movimento === 'Estorno') return 'success.main'; if (movimento === 'Entrada' || movimento === 'Estorno') {
return 'success.main';
}
if (movimento === 'TransferenciaInterna') {
return 'info.main';
}
return 'error.main'; return 'error.main';
} }
@ -95,7 +102,15 @@ function isMovimentoEntrada(movimento: string) {
} }
function movimentoBorderColor(movimento: string) { function movimentoBorderColor(movimento: string) {
return isMovimentoEntrada(movimento) ? 'success.main' : 'error.main'; if (isMovimentoEntrada(movimento)) {
return 'success.main';
}
if (movimento === 'TransferenciaInterna') {
return 'info.main';
}
return 'error.main';
} }
function movimentoActionLabel(item: DashboardMovimentoResumo) { function movimentoActionLabel(item: DashboardMovimentoResumo) {
@ -213,7 +228,15 @@ function StatCard({
} }
function MovimentoLinha({ item }: { item: DashboardMovimentoResumo }) { function MovimentoLinha({ item }: { item: DashboardMovimentoResumo }) {
const isEntrada = isMovimentoEntrada(item.movimento); const movimentoVisual =
item.movimento_dashboard || item.movimento;
const isTransferenciaInterna =
movimentoVisual === 'TransferenciaInterna';
const isEntrada =
movimentoVisual === 'Entrada'
|| movimentoVisual === 'Estorno';
return ( return (
<Box <Box
@ -224,8 +247,10 @@ function MovimentoLinha({ item }: { item: DashboardMovimentoResumo }) {
border: '1px solid', border: '1px solid',
borderColor: 'divider', borderColor: 'divider',
borderLeft: '6px solid', borderLeft: '6px solid',
borderLeftColor: movimentoBorderColor(item.movimento), borderLeftColor: movimentoBorderColor(movimentoVisual),
background: isEntrada background: isTransferenciaInterna
? 'linear-gradient(90deg, rgba(2,136,209,0.08) 0%, rgba(255,255,255,0.9) 42%)'
: isEntrada
? 'linear-gradient(90deg, rgba(46,125,50,0.08) 0%, rgba(255,255,255,0.9) 42%)' ? 'linear-gradient(90deg, rgba(46,125,50,0.08) 0%, rgba(255,255,255,0.9) 42%)'
: 'linear-gradient(90deg, rgba(211,47,47,0.08) 0%, rgba(255,255,255,0.9) 42%)', : 'linear-gradient(90deg, rgba(211,47,47,0.08) 0%, rgba(255,255,255,0.9) 42%)',
}} }}
@ -241,9 +266,19 @@ function MovimentoLinha({ item }: { item: DashboardMovimentoResumo }) {
</Typography> </Typography>
<Chip <Chip
label={item.movimento} label={
isTransferenciaInterna
? 'Transferência interna'
: movimentoVisual
}
color={
isTransferenciaInterna
? 'info'
: isEntrada
? 'success'
: 'error'
}
size="small" size="small"
color={isEntrada ? 'success' : 'error'}
variant="outlined" variant="outlined"
/> />
</Stack> </Stack>
@ -276,7 +311,7 @@ function MovimentoLinha({ item }: { item: DashboardMovimentoResumo }) {
spacing={0.75} spacing={0.75}
flexShrink={0} flexShrink={0}
> >
<Typography fontWeight={950} color={movimentoColor(item.movimento)}> <Typography fontWeight={950} color={movimentoColor(movimentoVisual)}>
{formatarValor(item.valor)} {formatarValor(item.valor)}
</Typography> </Typography>
@ -292,7 +327,13 @@ function MovimentoLinha({ item }: { item: DashboardMovimentoResumo }) {
to={`/movimentos/${item.idcontasapagar}/editar`} to={`/movimentos/${item.idcontasapagar}/editar`}
size="small" size="small"
variant="contained" variant="contained"
color={isEntrada ? 'success' : 'error'} color={
isTransferenciaInterna
? 'info'
: isEntrada
? 'success'
: 'error'
}
sx={{ borderRadius: 2, boxShadow: 0 }} sx={{ borderRadius: 2, boxShadow: 0 }}
> >
{movimentoActionLabel(item)} {movimentoActionLabel(item)}
@ -833,7 +874,7 @@ export function DashboardPage() {
<StatCard <StatCard
title="Investimentos" title="Investimentos"
value={formatarValor(cards.investimentos)} value={formatarValor(cards.investimentos)}
subtitle="Sangrias marcadas como investimento" subtitle="Valores destinados a investimentos"
icon={<AccountBalanceWalletIcon />} icon={<AccountBalanceWalletIcon />}
color="info" color="info"
percent={cards.percentualInvestimentos} percent={cards.percentualInvestimentos}

View File

@ -70,6 +70,9 @@ export type DashboardMovimentoResumo = {
idcentrodecustos?: number | null; idcentrodecustos?: number | null;
centro_custo_descricao?: string | null; centro_custo_descricao?: string | null;
dias_para_vencer?: number; dias_para_vencer?: number;
movimento_dashboard?: string;
direcao_dashboard?: string;
transferencia_interna?: boolean;
}; };
export type DashboardCredito = { export type DashboardCredito = {

View File

@ -144,10 +144,57 @@ function campoDataLabel(campo: DataCampo) {
return labels[campo]; return labels[campo];
} }
function getMovimentoVisual(item: Movimento) {
return item.movimento_dashboard || item.movimento;
}
function movimentoEhEntrada(item: Movimento) {
return item.direcao_dashboard
? item.direcao_dashboard === 'entrada'
: item.movimento === 'Entrada'
|| item.movimento === 'Estorno';
}
function movimentoEhSaida(item: Movimento) {
return item.direcao_dashboard
? item.direcao_dashboard === 'saida'
: item.movimento === 'Saida';
}
function movimentoEhTransferenciaInterna(item: Movimento) {
return item.transferencia_interna === true
|| item.direcao_dashboard === 'transferencia_interna'
|| getMovimentoVisual(item) === 'TransferenciaInterna';
}
function movimentoEhInvestimento(item: Movimento) {
return item.investimento_dashboard === true;
}
function movimentoChipColor(movimento: string) { function movimentoChipColor(movimento: string) {
if (movimento === 'Entrada') return 'success'; if (movimento === 'Entrada' || movimento === 'Estorno') {
if (movimento === 'Saida') return 'error'; return 'success';
if (movimento === 'Sangria') return 'warning'; }
if (movimento === 'Saida' || movimento === 'Saída') {
return 'error';
}
if (
movimento === 'TransferenciaInterna'
|| movimento === 'Transferência interna'
) {
return 'info';
}
if (movimento === 'Investimento') {
return 'primary';
}
if (movimento === 'Sangria') {
return 'warning';
}
return 'default'; return 'default';
} }
@ -264,7 +311,7 @@ function getMovimentoRowVisual(item: Movimento) {
}; };
} }
function getMovimentoIconVisual(movimento: MovimentoTipo) { function getMovimentoIconVisual(movimento: string) {
if (movimento === 'Entrada') { if (movimento === 'Entrada') {
return { return {
icon: <ArrowDownwardIcon fontSize="small" />, icon: <ArrowDownwardIcon fontSize="small" />,
@ -273,7 +320,7 @@ function getMovimentoIconVisual(movimento: MovimentoTipo) {
}; };
} }
if (movimento === 'Saida') { if (movimento === 'Saida' || movimento === 'Saída') {
return { return {
icon: <ArrowUpwardIcon fontSize="small" />, icon: <ArrowUpwardIcon fontSize="small" />,
label: 'Saída', label: 'Saída',
@ -281,6 +328,25 @@ function getMovimentoIconVisual(movimento: MovimentoTipo) {
}; };
} }
if (
movimento === 'TransferenciaInterna'
|| movimento === 'Transferência interna'
) {
return {
icon: <SwapHorizIcon fontSize="small" />,
label: 'Transferência interna',
color: 'info' as const,
};
}
if (movimento === 'Investimento') {
return {
icon: <AccountBalanceIcon fontSize="small" />,
label: 'Investimento',
color: 'primary' as const,
};
}
if (movimento === 'Sangria') { if (movimento === 'Sangria') {
return { return {
icon: <SwapHorizIcon fontSize="small" />, icon: <SwapHorizIcon fontSize="small" />,
@ -304,6 +370,24 @@ function getMovimentoIconVisual(movimento: MovimentoTipo) {
}; };
} }
function movimentoVisualLabel(item: Movimento) {
if (movimentoEhInvestimento(item)) {
return 'Investimento';
}
const movimento = getMovimentoVisual(item);
if (movimento === 'TransferenciaInterna') {
return 'Transferência interna';
}
if (movimento === 'Saida') {
return 'Saída';
}
return movimento;
}
function formatarDataGrupo(dataISO: string) { function formatarDataGrupo(dataISO: string) {
if (!dataISO || dataISO === 'sem-data') return 'Sem data'; if (!dataISO || dataISO === 'sem-data') return 'Sem data';
@ -329,13 +413,21 @@ function getDataGrupo(item: Movimento, campo: DataCampo) {
function valorAssinadoMovimento(item: Movimento) { function valorAssinadoMovimento(item: Movimento) {
const valor = Number(item.valor || 0); const valor = Number(item.valor || 0);
if (item.movimento === 'Entrada' || item.movimento === 'Estorno') { if (movimentoEhTransferenciaInterna(item)) {
return 0;
}
if (movimentoEhEntrada(item)) {
return valor; return valor;
} }
if (movimentoEhSaida(item) || movimentoEhInvestimento(item)) {
return valor * -1; return valor * -1;
} }
return 0;
}
function criarResumoGrupo() { function criarResumoGrupo() {
return { return {
totalEntradas: 0, totalEntradas: 0,
@ -345,15 +437,15 @@ function criarResumoGrupo() {
}; };
} }
function aplicarMovimentoNoResumo<T extends ReturnType<typeof criarResumoGrupo>>( function aplicarMovimentoNoResumo<T extends ReturnType<typeof criarResumoGrupo>>(resumo: T, item: Movimento) {
resumo: T,
item: Movimento
) {
const valorAssinado = valorAssinadoMovimento(item); const valorAssinado = valorAssinadoMovimento(item);
if (valorAssinado >= 0) { if (movimentoEhEntrada(item)) {
resumo.totalEntradas += valorAssinado; resumo.totalEntradas += Math.abs(valorAssinado);
} else { } else if (
movimentoEhSaida(item)
|| movimentoEhInvestimento(item)
) {
resumo.totalSaidas += Math.abs(valorAssinado); resumo.totalSaidas += Math.abs(valorAssinado);
} }
@ -377,10 +469,24 @@ function agruparMovimentos(movimentos: Movimento[], campo: DataCampo): GrupoDiaM
for (const item of movimentos) { for (const item of movimentos) {
const chaveDia = getDataGrupo(item, campo); const chaveDia = getDataGrupo(item, campo);
const bancoNome = item.banco_descricao || 'Sem banco'; const bancoNome =
item.direcao_dashboard === 'entrada'
&& item.movimento === 'Sangria'
? item.banco_referencia_descricao
|| item.banco_descricao
|| 'Sem banco'
: item.banco_descricao
|| 'Sem banco';
const centroNome = item.centro_custo_descricao || 'Sem centro de custo'; const centroNome = item.centro_custo_descricao || 'Sem centro de custo';
const chaveBanco = String(item.idbancos || bancoNome); const idBancoVisual =
item.direcao_dashboard === 'entrada'
&& item.movimento === 'Sangria'
? item.idbancos_p
: item.idbancos;
const chaveBanco =
String(idBancoVisual || bancoNome);
const chaveCentro = String(item.idcentrodecustos || centroNome); const chaveCentro = String(item.idcentrodecustos || centroNome);
if (!gruposDia.has(chaveDia)) { if (!gruposDia.has(chaveDia)) {
@ -473,17 +579,41 @@ export function MovimentosPage() {
page: 1, page: 1,
totalPages: 1, totalPages: 1,
}); });
const [summary, setSummary] = useState<MovimentosSummary>({
const SUMMARY_INICIAL: MovimentosSummary = {
quantidade: 0, quantidade: 0,
totalEntradas: 0, totalEntradas: 0,
entradasRecebidas: 0,
entradasPendentes: 0,
quantidadeEntradasRecebidas: 0,
quantidadeEntradasPendentes: 0,
percentualEntradasRecebidas: 0,
percentualEntradasPendentes: 0,
totalSaidas: 0, totalSaidas: 0,
totalSangrias: 0, saidasPagas: 0,
saidasPendentes: 0,
quantidadeSaidasPagas: 0,
quantidadeSaidasPendentes: 0,
percentualSaidasPagas: 0,
percentualSaidasPendentes: 0,
totalInvestimentos: 0,
investimentosRealizados: 0,
investimentosPendentes: 0,
quantidadeInvestimentosRealizados: 0,
quantidadeInvestimentosPendentes: 0,
percentualInvestimentosRealizados: 0,
percentualInvestimentosPendentes: 0,
totalEstornos: 0, totalEstornos: 0,
totalBaixado: 0,
totalAberto: 0,
saldoProcessado: 0, saldoProcessado: 0,
saldo: 0, saldo: 0,
}); };
const [summary, setSummary] =
useState<MovimentosSummary>(SUMMARY_INICIAL);
const [modoVisualizacao, setModoVisualizacao] = useState<ModoVisualizacao>('tabela'); const [modoVisualizacao, setModoVisualizacao] = useState<ModoVisualizacao>('tabela');
const [gruposAbertos, setGruposAbertos] = useState<Record<string, boolean>>({}); const [gruposAbertos, setGruposAbertos] = useState<Record<string, boolean>>({});
@ -968,7 +1098,8 @@ export function MovimentosPage() {
<Stack spacing={0.75} padding={1.25} paddingTop={0}> <Stack spacing={0.75} padding={1.25} paddingTop={0}>
{centro.movimentos.map((item) => { {centro.movimentos.map((item) => {
const rowVisual = getMovimentoRowVisual(item); const rowVisual = getMovimentoRowVisual(item);
const movimentoIcon = getMovimentoIconVisual(item.movimento); const movimentoVisual = movimentoVisualLabel(item);
const movimentoIcon = getMovimentoIconVisual(movimentoVisual);
const dataPrincipal = getDataPrincipal(item, dataCampo); const dataPrincipal = getDataPrincipal(item, dataCampo);
return ( return (
@ -1046,13 +1177,17 @@ export function MovimentosPage() {
<Typography <Typography
fontWeight={950} fontWeight={950}
color={ color={
item.movimento === 'Entrada' || item.movimento === 'Estorno' movimentoEhTransferenciaInterna(item)
? 'info.main'
: movimentoEhEntrada(item)
? 'success.main' ? 'success.main'
: 'error.main' : 'error.main'
} }
whiteSpace="nowrap" whiteSpace="nowrap"
> >
{item.movimento === 'Entrada' || item.movimento === 'Estorno' {movimentoEhTransferenciaInterna(item)
? ''
: movimentoEhEntrada(item)
? '+' ? '+'
: '-'} : '-'}
{formatarValor(item.valor)} {formatarValor(item.valor)}
@ -1094,6 +1229,133 @@ export function MovimentosPage() {
); );
} }
function CardResumoFinanceiro({
titulo,
total,
realizadoLabel,
realizado,
realizadoQuantidade,
realizadoPercentual,
pendenteLabel,
pendente,
pendenteQuantidade,
pendentePercentual,
corTotal,
}: {
titulo: string;
total: number;
realizadoLabel: string;
realizado: number;
realizadoQuantidade: number;
realizadoPercentual: number;
pendenteLabel: string;
pendente: number;
pendenteQuantidade: number;
pendentePercentual: number;
corTotal: 'success.main' | 'error.main' | 'primary.main';
}) {
return (
<Card>
<CardContent>
<Typography
variant="body2"
color="text.secondary"
>
{titulo}
</Typography>
<Typography
variant="h5"
fontWeight={950}
color={corTotal}
marginTop={0.25}
>
{formatarValor(total)}
</Typography>
<Divider sx={{ marginY: 1.25 }} />
<Stack spacing={0.75}>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
spacing={1}
>
<Box>
<Typography
variant="caption"
color="text.secondary"
>
{realizadoLabel}
</Typography>
<Typography
variant="body2"
fontWeight={850}
>
{formatarValor(realizado)}
</Typography>
</Box>
<Chip
size="small"
color="success"
variant="outlined"
label={`${realizadoPercentual.toFixed(1)}%`}
/>
</Stack>
<Typography
variant="caption"
color="text.secondary"
>
{realizadoQuantidade} movimento(s)
</Typography>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
spacing={1}
marginTop={0.5}
>
<Box>
<Typography
variant="caption"
color="text.secondary"
>
{pendenteLabel}
</Typography>
<Typography
variant="body2"
fontWeight={850}
>
{formatarValor(pendente)}
</Typography>
</Box>
<Chip
size="small"
color="warning"
variant="outlined"
label={`${pendentePercentual.toFixed(1)}%`}
/>
</Stack>
<Typography
variant="caption"
color="text.secondary"
>
{pendenteQuantidade} movimento(s)
</Typography>
</Stack>
</CardContent>
</Card>
);
}
return ( return (
<Box> <Box>
<Stack <Stack
@ -1153,77 +1415,132 @@ export function MovimentosPage() {
display="grid" display="grid"
gridTemplateColumns={{ gridTemplateColumns={{
xs: '1fr', xs: '1fr',
md: 'repeat(6, 1fr)', md: 'repeat(2, minmax(0, 1fr))',
xl: 'repeat(5, minmax(0, 1fr))',
}} }}
gap={{ xs: 2, md: 2.5 }} gap={{ xs: 2, md: 2.5 }}
marginBottom={{ xs: 2.5, md: 3 }} marginBottom={{ xs: 2.5, md: 3 }}
> >
<Card> <Card>
<CardContent> <CardContent>
<Typography variant="body2" color="text.secondary"> <Typography
variant="body2"
color="text.secondary"
>
Registros encontrados Registros encontrados
</Typography> </Typography>
<Typography variant="h5" fontWeight={950}>
{summary.quantidade}
</Typography>
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="body2" color="text.secondary">
Entradas
</Typography>
<Typography variant="h5" fontWeight={950} color="success.main">
{formatarValor(summary.totalEntradas)}
</Typography>
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="body2" color="text.secondary">
Saídas
</Typography>
<Typography variant="h5" fontWeight={950} color="error.main">
{formatarValor(summary.totalSaidas + summary.totalSangrias)}
</Typography>
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="body2" color="text.secondary">
Saldo filtrado
</Typography>
<Typography <Typography
variant="h5" variant="h5"
fontWeight={950} fontWeight={950}
color={summary.saldo >= 0 ? 'success.main' : 'error.main'} >
{summary.quantidade}
</Typography>
<Typography
variant="caption"
color="text.secondary"
>
Movimentos financeiros filtrados
</Typography>
</CardContent>
</Card>
<CardResumoFinanceiro
titulo="Entradas"
total={summary.totalEntradas}
realizadoLabel="Recebido"
realizado={summary.entradasRecebidas}
realizadoQuantidade={
summary.quantidadeEntradasRecebidas
}
realizadoPercentual={
summary.percentualEntradasRecebidas
}
pendenteLabel="A receber"
pendente={summary.entradasPendentes}
pendenteQuantidade={
summary.quantidadeEntradasPendentes
}
pendentePercentual={
summary.percentualEntradasPendentes
}
corTotal="success.main"
/>
<CardResumoFinanceiro
titulo="Saídas"
total={summary.totalSaidas}
realizadoLabel="Pago"
realizado={summary.saidasPagas}
realizadoQuantidade={
summary.quantidadeSaidasPagas
}
realizadoPercentual={
summary.percentualSaidasPagas
}
pendenteLabel="A pagar"
pendente={summary.saidasPendentes}
pendenteQuantidade={
summary.quantidadeSaidasPendentes
}
pendentePercentual={
summary.percentualSaidasPendentes
}
corTotal="error.main"
/>
<CardResumoFinanceiro
titulo="Investimentos"
total={summary.totalInvestimentos}
realizadoLabel="Realizado"
realizado={summary.investimentosRealizados}
realizadoQuantidade={
summary.quantidadeInvestimentosRealizados
}
realizadoPercentual={
summary.percentualInvestimentosRealizados
}
pendenteLabel="Pendente"
pendente={summary.investimentosPendentes}
pendenteQuantidade={
summary.quantidadeInvestimentosPendentes
}
pendentePercentual={
summary.percentualInvestimentosPendentes
}
corTotal="primary.main"
/>
<Card>
<CardContent>
<Typography
variant="body2"
color="text.secondary"
>
Saldo filtrado
</Typography>
<Typography
variant="h5"
fontWeight={950}
color={
summary.saldo >= 0
? 'success.main'
: 'error.main'
}
marginTop={0.25}
> >
{formatarValor(summary.saldo)} {formatarValor(summary.saldo)}
</Typography> </Typography>
</CardContent>
</Card>
<Card> <Divider sx={{ marginY: 1.25 }} />
<CardContent>
<Typography variant="body2" color="text.secondary">
Em aberto
</Typography>
<Typography variant="h5" fontWeight={950} color="warning.main">
{formatarValor(summary.totalAberto)}
</Typography>
</CardContent>
</Card>
<Card> <Typography
<CardContent> variant="caption"
<Typography variant="body2" color="text.secondary"> color="text.secondary"
Baixados >
</Typography> Entradas menos saídas e investimentos
<Typography variant="h5" fontWeight={950} color="info.main">
{formatarValor(summary.totalBaixado)}
</Typography> </Typography>
</CardContent> </CardContent>
</Card> </Card>
@ -1553,7 +1870,8 @@ export function MovimentosPage() {
{movimentos.map((item) => { {movimentos.map((item) => {
const dataPrincipal = getDataPrincipal(item, dataCampo); const dataPrincipal = getDataPrincipal(item, dataCampo);
const rowVisual = getMovimentoRowVisual(item); const rowVisual = getMovimentoRowVisual(item);
const movimentoIcon = getMovimentoIconVisual(item.movimento); const movimentoVisual = movimentoVisualLabel(item);
const movimentoIcon = getMovimentoIconVisual(movimentoVisual);
return ( return (
<Box <Box
@ -1609,8 +1927,13 @@ export function MovimentosPage() {
<Typography <Typography
fontWeight={950} fontWeight={950}
color={ color={
item.movimento === 'Entrada' || item.movimento === 'Estorno' movimentoEhTransferenciaInterna(item)
? 'info.main'
: movimentoEhEntrada(item)
? 'success.main' ? 'success.main'
: movimentoEhSaida(item)
|| movimentoEhInvestimento(item)
? 'error.main'
: 'text.primary' : 'text.primary'
} }
whiteSpace="nowrap" whiteSpace="nowrap"
@ -1621,9 +1944,11 @@ export function MovimentosPage() {
<Stack direction="row" flexWrap="wrap" spacing={1} useFlexGap> <Stack direction="row" flexWrap="wrap" spacing={1} useFlexGap>
<Chip <Chip
label={item.movimento} label={movimentoVisual}
size="small" size="small"
color={movimentoChipColor(item.movimento) as any} color={
movimentoChipColor(movimentoVisual) as any
}
/> />
<Chip <Chip
@ -1749,7 +2074,8 @@ export function MovimentosPage() {
{movimentos.map((item) => { {movimentos.map((item) => {
const dataPrincipal = getDataPrincipal(item, dataCampo); const dataPrincipal = getDataPrincipal(item, dataCampo);
const rowVisual = getMovimentoRowVisual(item); const rowVisual = getMovimentoRowVisual(item);
const movimentoIcon = getMovimentoIconVisual(item.movimento); const movimentoVisual = movimentoVisualLabel(item);
const movimentoIcon = getMovimentoIconVisual(movimentoVisual);
return ( return (
<TableRow <TableRow
@ -1825,8 +2151,13 @@ export function MovimentosPage() {
<Typography <Typography
fontWeight={950} fontWeight={950}
color={ color={
item.movimento === 'Entrada' || item.movimento === 'Estorno' movimentoEhTransferenciaInterna(item)
? 'info.main'
: movimentoEhEntrada(item)
? 'success.main' ? 'success.main'
: movimentoEhSaida(item)
|| movimentoEhInvestimento(item)
? 'error.main'
: 'text.primary' : 'text.primary'
} }
> >
@ -1853,9 +2184,11 @@ export function MovimentosPage() {
<TableCell> <TableCell>
<Chip <Chip
label={item.movimento} label={movimentoVisual}
size="small" size="small"
color={movimentoChipColor(item.movimento) as any} color={
movimentoChipColor(movimentoVisual) as any
}
/> />
</TableCell> </TableCell>

View File

@ -43,6 +43,15 @@ export type Movimento = {
banco_referencia_debito?: number | null; banco_referencia_debito?: number | null;
movimento_fixo_descricao?: string | null; movimento_fixo_descricao?: string | null;
ocorrencia_extrato?: 'lancamento' | 'baixa' | null; ocorrencia_extrato?: 'lancamento' | 'baixa' | null;
movimento_dashboard?: string;
direcao_dashboard?: 'entrada' | 'saida' | 'transferencia_interna' | 'neutro';
transferencia_interna?: boolean;
investimento_dashboard?: boolean;
idusuario_origem?: number | null;
idusuario_destino?: number | null;
banco_destino_investimento?: number | null;
}; };
export type CriarMovimentoRequest = { export type CriarMovimentoRequest = {
@ -115,14 +124,41 @@ export type MovimentosPagination = {
export type MovimentosSummary = { export type MovimentosSummary = {
quantidade: number; quantidade: number;
totalEntradas: number; totalEntradas: number;
entradasRecebidas: number;
entradasPendentes: number;
quantidadeEntradasRecebidas: number;
quantidadeEntradasPendentes: number;
percentualEntradasRecebidas: number;
percentualEntradasPendentes: number;
totalSaidas: number; totalSaidas: number;
totalSangrias: number; saidasPagas: number;
saidasPendentes: number;
quantidadeSaidasPagas: number;
quantidadeSaidasPendentes: number;
percentualSaidasPagas: number;
percentualSaidasPendentes: number;
totalInvestimentos: number;
investimentosRealizados: number;
investimentosPendentes: number;
quantidadeInvestimentosRealizados: number;
quantidadeInvestimentosPendentes: number;
percentualInvestimentosRealizados: number;
percentualInvestimentosPendentes: number;
totalEstornos: number; totalEstornos: number;
totalBaixado: number;
totalAberto: number;
saldoProcessado: number; saldoProcessado: number;
saldo: number; saldo: number;
/*
* Compatibilidade temporária com versões antigas da API.
*/
totalSangrias?: number;
totalBaixado?: number;
totalAberto?: number;
}; };
export type ApiResponse<T> = { export type ApiResponse<T> = {