criado agrupamento por data de extrato
This commit is contained in:
parent
759e642567
commit
09ef64f491
|
|
@ -29,6 +29,8 @@ const CAMPOS_MOVIMENTO_SELECT = `
|
|||
cp.insert_date,
|
||||
cp.update_date,
|
||||
cp.deleted_at,
|
||||
NULL AS data_extrato,
|
||||
NULL AS ocorrencia_extrato,
|
||||
b.descricao AS banco_descricao,
|
||||
b.debito AS banco_debito,
|
||||
cc.descricao AS centro_custo_descricao,
|
||||
|
|
@ -47,6 +49,48 @@ const FROM_MOVIMENTO_JOIN = `
|
|||
LEFT JOIN movimentosfixos mf ON mf.idmovimentosfixos = cp.idmovimentosfixos
|
||||
`;
|
||||
|
||||
function montarSelectExtrato(campoDataSql, ocorrencia) {
|
||||
return `
|
||||
SELECT
|
||||
cp.idcontasapagar,
|
||||
cp.movimento,
|
||||
cp.descricao,
|
||||
cp.dataentrada,
|
||||
cp.datavencimento,
|
||||
cp.databaixa,
|
||||
cp.valor,
|
||||
cp.parcela,
|
||||
cp.parcelas,
|
||||
cp.status,
|
||||
cp.idcentrodecustos,
|
||||
cp.idbancos,
|
||||
cp.idusuarios_cad,
|
||||
cp.idusuarios_baixa,
|
||||
cp.idclientes,
|
||||
cp.idveiculosdetalhes,
|
||||
cp.idbancos_p,
|
||||
cp.idmovimentosfixos,
|
||||
cp.competencia,
|
||||
cp.grupo_parcelamento,
|
||||
cp.origem,
|
||||
cp.saldo_processado,
|
||||
cp.observacao,
|
||||
cp.insert_date,
|
||||
cp.update_date,
|
||||
cp.deleted_at,
|
||||
DATE(${campoDataSql}) AS data_extrato,
|
||||
'${ocorrencia}' AS ocorrencia_extrato,
|
||||
b.descricao AS banco_descricao,
|
||||
b.debito AS banco_debito,
|
||||
cc.descricao AS centro_custo_descricao,
|
||||
c.nome AS cliente_nome,
|
||||
bp.descricao AS banco_referencia_descricao,
|
||||
bp.debito AS banco_referencia_debito,
|
||||
mf.descricao AS movimento_fixo_descricao
|
||||
${FROM_MOVIMENTO_JOIN}
|
||||
`;
|
||||
}
|
||||
|
||||
function normalizarNumeroOuNull(valor) {
|
||||
if (valor === undefined || valor === null || valor === '') {
|
||||
return null;
|
||||
|
|
@ -189,6 +233,8 @@ function resolverOrdenacao(orderBy) {
|
|||
banco_descricao: 'b.descricao',
|
||||
centro_custo_descricao: 'cc.descricao',
|
||||
cliente_nome: 'c.nome',
|
||||
extrato: 'data_extrato',
|
||||
data_extrato: 'data_extrato',
|
||||
};
|
||||
|
||||
return camposPermitidos[orderBy] || 'cp.datavencimento';
|
||||
|
|
@ -199,6 +245,28 @@ function resolverDirecao(orderDirection) {
|
|||
}
|
||||
|
||||
function montarWhereMovimentos(filtros = {}) {
|
||||
const { where, params } = montarWhereMovimentosBase(filtros);
|
||||
const campoData = resolverCampoData(filtros.dataCampo);
|
||||
|
||||
if (filtros.dataInicio) {
|
||||
where.push(`DATE(${campoData}) >= ?`);
|
||||
params.push(filtros.dataInicio);
|
||||
}
|
||||
|
||||
if (filtros.dataFim) {
|
||||
where.push(`DATE(${campoData}) <= ?`);
|
||||
params.push(filtros.dataFim);
|
||||
}
|
||||
|
||||
const whereSql = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
|
||||
|
||||
return {
|
||||
whereSql,
|
||||
params,
|
||||
};
|
||||
}
|
||||
|
||||
function montarWhereMovimentosBase(filtros = {}) {
|
||||
const where = [];
|
||||
const params = [];
|
||||
|
||||
|
|
@ -261,7 +329,11 @@ function montarWhereMovimentos(filtros = {}) {
|
|||
params.push(filtros.origem);
|
||||
}
|
||||
|
||||
if (filtros.saldo_processado !== undefined && filtros.saldo_processado !== null && filtros.saldo_processado !== '') {
|
||||
if (
|
||||
filtros.saldo_processado !== undefined &&
|
||||
filtros.saldo_processado !== null &&
|
||||
filtros.saldo_processado !== ''
|
||||
) {
|
||||
where.push('cp.saldo_processado = ?');
|
||||
params.push(Number(filtros.saldo_processado));
|
||||
}
|
||||
|
|
@ -278,18 +350,6 @@ function montarWhereMovimentos(filtros = {}) {
|
|||
where.push('cp.idclientes IS NULL AND cp.idbancos_p IS NULL');
|
||||
}
|
||||
|
||||
const campoData = resolverCampoData(filtros.dataCampo);
|
||||
|
||||
if (filtros.dataInicio) {
|
||||
where.push(`DATE(${campoData}) >= ?`);
|
||||
params.push(filtros.dataInicio);
|
||||
}
|
||||
|
||||
if (filtros.dataFim) {
|
||||
where.push(`DATE(${campoData}) <= ?`);
|
||||
params.push(filtros.dataFim);
|
||||
}
|
||||
|
||||
if (filtros.busca) {
|
||||
where.push(`
|
||||
(
|
||||
|
|
@ -322,15 +382,158 @@ function montarWhereMovimentos(filtros = {}) {
|
|||
);
|
||||
}
|
||||
|
||||
const whereSql = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
|
||||
|
||||
return {
|
||||
whereSql,
|
||||
where,
|
||||
params,
|
||||
};
|
||||
}
|
||||
|
||||
async function listarMovimentosExtrato(filtros = {}) {
|
||||
const limite = limitarNumero(filtros.limite, 20, 1, 500);
|
||||
const page = limitarNumero(filtros.page, 1, 1, 999999);
|
||||
const offset = filtros.offset !== undefined
|
||||
? limitarNumero(filtros.offset, 0, 0, 999999999)
|
||||
: (page - 1) * limite;
|
||||
|
||||
const orderDirection = resolverDirecao(filtros.orderDirection);
|
||||
|
||||
const base = montarWhereMovimentosBase(filtros);
|
||||
|
||||
function montarWhereOcorrencia(campoDataSql, extras = []) {
|
||||
const where = [...base.where];
|
||||
|
||||
where.push(`${campoDataSql} IS NOT NULL`);
|
||||
|
||||
if (filtros.dataInicio) {
|
||||
where.push(`DATE(${campoDataSql}) >= ?`);
|
||||
}
|
||||
|
||||
if (filtros.dataFim) {
|
||||
where.push(`DATE(${campoDataSql}) <= ?`);
|
||||
}
|
||||
|
||||
where.push(...extras);
|
||||
|
||||
const params = [...base.params];
|
||||
|
||||
if (filtros.dataInicio) {
|
||||
params.push(filtros.dataInicio);
|
||||
}
|
||||
|
||||
if (filtros.dataFim) {
|
||||
params.push(filtros.dataFim);
|
||||
}
|
||||
|
||||
return {
|
||||
whereSql: where.length > 0 ? `WHERE ${where.join(' AND ')}` : '',
|
||||
params,
|
||||
};
|
||||
}
|
||||
|
||||
const lancamento = montarWhereOcorrencia('cp.dataentrada');
|
||||
|
||||
const baixa = montarWhereOcorrencia('cp.databaixa', [
|
||||
`(
|
||||
cp.dataentrada IS NULL
|
||||
OR DATE(cp.databaixa) <> DATE(cp.dataentrada)
|
||||
)`,
|
||||
]);
|
||||
|
||||
const selectLancamento = `
|
||||
${montarSelectExtrato('cp.dataentrada', 'lancamento')}
|
||||
${lancamento.whereSql}
|
||||
`;
|
||||
|
||||
const selectBaixa = `
|
||||
${montarSelectExtrato('cp.databaixa', 'baixa')}
|
||||
${baixa.whereSql}
|
||||
`;
|
||||
|
||||
const unionSql = `
|
||||
(
|
||||
${selectLancamento}
|
||||
UNION ALL
|
||||
${selectBaixa}
|
||||
) extrato
|
||||
`;
|
||||
|
||||
const paramsUnion = [
|
||||
...lancamento.params,
|
||||
...baixa.params,
|
||||
];
|
||||
|
||||
const [rows] = await pool.query(
|
||||
`
|
||||
SELECT *
|
||||
FROM ${unionSql}
|
||||
ORDER BY data_extrato ${orderDirection}, idcontasapagar DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`,
|
||||
[...paramsUnion, limite, offset]
|
||||
);
|
||||
|
||||
const [countRows] = await pool.query(
|
||||
`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM ${unionSql}
|
||||
`,
|
||||
paramsUnion
|
||||
);
|
||||
|
||||
const [summaryRows] = await pool.query(
|
||||
`
|
||||
SELECT
|
||||
COUNT(*) AS quantidade,
|
||||
COALESCE(SUM(CASE WHEN movimento = 'Entrada' THEN valor ELSE 0 END), 0) AS totalEntradas,
|
||||
COALESCE(SUM(CASE WHEN movimento = 'Saida' THEN valor ELSE 0 END), 0) AS totalSaidas,
|
||||
COALESCE(SUM(CASE WHEN movimento = 'Sangria' THEN valor ELSE 0 END), 0) AS totalSangrias,
|
||||
COALESCE(SUM(CASE WHEN movimento = 'Estorno' THEN valor ELSE 0 END), 0) AS totalEstornos,
|
||||
COALESCE(SUM(CASE WHEN status IN ('Pago', 'Recebido') THEN valor ELSE 0 END), 0) AS totalBaixado,
|
||||
COALESCE(SUM(CASE WHEN status IN ('A pagar', 'A receber') THEN valor ELSE 0 END), 0) AS totalAberto,
|
||||
COALESCE(SUM(CASE WHEN saldo_processado = 1 THEN 1 ELSE 0 END), 0) AS saldoProcessado,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN movimento IN ('Entrada', 'Estorno') THEN valor
|
||||
WHEN movimento IN ('Saida', 'Sangria') THEN -valor
|
||||
ELSE 0
|
||||
END
|
||||
), 0) AS saldo
|
||||
FROM ${unionSql}
|
||||
`,
|
||||
paramsUnion
|
||||
);
|
||||
|
||||
const total = Number(countRows[0]?.total || 0);
|
||||
const totalPages = Math.max(1, Math.ceil(total / limite));
|
||||
|
||||
return {
|
||||
data: rows,
|
||||
pagination: {
|
||||
total,
|
||||
limite,
|
||||
offset,
|
||||
page: Math.floor(offset / limite) + 1,
|
||||
totalPages,
|
||||
},
|
||||
summary: {
|
||||
quantidade: Number(summaryRows[0]?.quantidade || 0),
|
||||
totalEntradas: Number(summaryRows[0]?.totalEntradas || 0),
|
||||
totalSaidas: Number(summaryRows[0]?.totalSaidas || 0),
|
||||
totalSangrias: Number(summaryRows[0]?.totalSangrias || 0),
|
||||
totalEstornos: Number(summaryRows[0]?.totalEstornos || 0),
|
||||
totalBaixado: Number(summaryRows[0]?.totalBaixado || 0),
|
||||
totalAberto: Number(summaryRows[0]?.totalAberto || 0),
|
||||
saldoProcessado: Number(summaryRows[0]?.saldoProcessado || 0),
|
||||
saldo: Number(summaryRows[0]?.saldo || 0),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function listarMovimentos(filtros = {}) {
|
||||
if (filtros.dataCampo === 'extrato') {
|
||||
return listarMovimentosExtrato(filtros);
|
||||
}
|
||||
|
||||
const limite = limitarNumero(filtros.limite, 20, 1, 100);
|
||||
const page = limitarNumero(filtros.page, 1, 1, 999999);
|
||||
const offset = filtros.offset !== undefined
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ function formatarData(data: string | null) {
|
|||
|
||||
function campoDataLabel(campo: DataCampo) {
|
||||
const labels: Record<DataCampo, string> = {
|
||||
extrato: 'Extrato',
|
||||
dataentrada: 'Entrada',
|
||||
datavencimento: 'Vencimento',
|
||||
databaixa: 'Baixa',
|
||||
|
|
@ -157,11 +158,16 @@ function statusChipColor(status: string) {
|
|||
}
|
||||
|
||||
function getDataPrincipal(item: Movimento, campo: DataCampo) {
|
||||
if (campo === 'extrato') {
|
||||
return item.data_extrato || item.databaixa || item.dataentrada;
|
||||
}
|
||||
|
||||
if (campo === 'dataentrada') return item.dataentrada;
|
||||
if (campo === 'datavencimento') return item.datavencimento;
|
||||
if (campo === 'databaixa') return item.databaixa;
|
||||
if (campo === 'insert_date') return item.insert_date;
|
||||
if (campo === 'update_date') return item.update_date;
|
||||
if (campo === 'deleted_at') return item.deleted_at;
|
||||
|
||||
return item.datavencimento;
|
||||
}
|
||||
|
|
@ -491,7 +497,7 @@ export function MovimentosPage() {
|
|||
const [erro, setErro] = useState('');
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [dataCampo, setDataCampo] = useState<DataCampo>('dataentrada');
|
||||
const [dataCampo, setDataCampo] = useState<DataCampo>('extrato');
|
||||
const [dataInicio, setDataInicio] = useState(inicioMesAtual());
|
||||
const [dataFim, setDataFim] = useState(fimMesAtual());
|
||||
const [busca, setBusca] = useState('');
|
||||
|
|
@ -568,7 +574,8 @@ export function MovimentosPage() {
|
|||
|
||||
async function carregarMovimentos(
|
||||
pageToLoad = page,
|
||||
modoParaCarregar = modoVisualizacao
|
||||
modoParaCarregar = modoVisualizacao,
|
||||
dataCampoParaCarregar = dataCampo
|
||||
) {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
|
@ -577,7 +584,7 @@ export function MovimentosPage() {
|
|||
const params: ListarMovimentosParams = {
|
||||
limite: modoParaCarregar === 'agrupado' ? LIMITE_AGRUPADO : LIMITE_PADRAO,
|
||||
page: modoParaCarregar === 'agrupado' ? 1 : pageToLoad,
|
||||
dataCampo,
|
||||
dataCampo: dataCampoParaCarregar,
|
||||
dataInicio,
|
||||
dataFim,
|
||||
busca: busca.trim() || undefined,
|
||||
|
|
@ -591,7 +598,7 @@ export function MovimentosPage() {
|
|||
origem: origem || undefined,
|
||||
saldo_processado: saldoProcessado,
|
||||
incluirExcluidos,
|
||||
orderBy: dataCampo,
|
||||
orderBy: dataCampoParaCarregar,
|
||||
orderDirection: 'DESC',
|
||||
};
|
||||
|
||||
|
|
@ -619,7 +626,7 @@ export function MovimentosPage() {
|
|||
}
|
||||
|
||||
function limparFiltros() {
|
||||
setDataCampo('dataentrada');
|
||||
setDataCampo('extrato');
|
||||
setDataInicio(inicioMesAtual());
|
||||
setDataFim(fimMesAtual());
|
||||
setBusca('');
|
||||
|
|
@ -668,6 +675,13 @@ export function MovimentosPage() {
|
|||
return Number(valor) === 1 ? 'Saldo processado' : 'Sem impacto';
|
||||
}
|
||||
|
||||
function ocorrenciaExtratoLabel(item: Movimento) {
|
||||
if (item.ocorrencia_extrato === 'baixa') return 'Baixa';
|
||||
if (item.ocorrencia_extrato === 'lancamento') return 'Lançamento';
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function saldoProcessadoColor(valor: number | null | undefined) {
|
||||
return Number(valor) === 1 ? 'success' : 'default';
|
||||
}
|
||||
|
|
@ -720,12 +734,18 @@ export function MovimentosPage() {
|
|||
function mudarModoVisualizacao(novoModo: ModoVisualizacao | null) {
|
||||
if (!novoModo) return;
|
||||
|
||||
const novoCampoData: DataCampo =
|
||||
novoModo === 'agrupado' && dataCampo === 'dataentrada'
|
||||
? 'extrato'
|
||||
: dataCampo;
|
||||
|
||||
setModoVisualizacao(novoModo);
|
||||
setDataCampo(novoCampoData);
|
||||
setGruposAbertos({});
|
||||
setPage(1);
|
||||
|
||||
setTimeout(() => {
|
||||
carregarMovimentos(1, novoModo);
|
||||
carregarMovimentos(1, novoModo, novoCampoData);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
|
@ -953,7 +973,7 @@ export function MovimentosPage() {
|
|||
|
||||
return (
|
||||
<Paper
|
||||
key={item.idcontasapagar}
|
||||
key={`${item.idcontasapagar}:${item.ocorrencia_extrato || 'normal'}:${getDataPrincipal(item, dataCampo) || ''}`}
|
||||
elevation={0}
|
||||
sx={{
|
||||
padding: 1.25,
|
||||
|
|
@ -1000,7 +1020,11 @@ export function MovimentosPage() {
|
|||
</Typography>
|
||||
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{campoDataLabel(dataCampo)}: {formatarData(dataPrincipal)} ·{' '}
|
||||
{campoDataLabel(dataCampo)}: {formatarData(dataPrincipal)}
|
||||
{dataCampo === 'extrato' && ocorrenciaExtratoLabel(item)
|
||||
? ` · ${ocorrenciaExtratoLabel(item)}`
|
||||
: ''}
|
||||
{' · '}
|
||||
Ref.: {getReferencia(item)} · {origemLabel(item.origem)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
|
@ -1248,6 +1272,7 @@ export function MovimentosPage() {
|
|||
onChange={(event) => setDataCampo(event.target.value as DataCampo)}
|
||||
fullWidth
|
||||
>
|
||||
<MenuItem value="extrato">Extrato real</MenuItem>
|
||||
<MenuItem value="datavencimento">Data de vencimento</MenuItem>
|
||||
<MenuItem value="dataentrada">Data de entrada</MenuItem>
|
||||
<MenuItem value="databaixa">Data de baixa</MenuItem>
|
||||
|
|
@ -1532,7 +1557,7 @@ export function MovimentosPage() {
|
|||
|
||||
return (
|
||||
<Box
|
||||
key={item.idcontasapagar}
|
||||
key={`${item.idcontasapagar}:${item.ocorrencia_extrato || 'normal'}:${getDataPrincipal(item, dataCampo) || ''}`}
|
||||
sx={{
|
||||
padding: 2,
|
||||
backgroundColor: rowVisual.backgroundColor,
|
||||
|
|
@ -1683,7 +1708,7 @@ export function MovimentosPage() {
|
|||
<Table
|
||||
size="small"
|
||||
sx={{
|
||||
minWidth: 1720,
|
||||
minWidth: 1840,
|
||||
'& th': {
|
||||
whiteSpace: 'nowrap',
|
||||
fontWeight: 900,
|
||||
|
|
@ -1697,6 +1722,7 @@ export function MovimentosPage() {
|
|||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ minWidth: 70 }} align="center">Tipo</TableCell>
|
||||
<TableCell sx={{ minWidth: 120 }}>Ocorrência</TableCell>
|
||||
<TableCell sx={{ minWidth: 260 }}>Descrição</TableCell>
|
||||
<TableCell sx={{ minWidth: 120 }}>Entrada</TableCell>
|
||||
<TableCell sx={{ minWidth: 130 }}>Vencimento</TableCell>
|
||||
|
|
@ -1727,7 +1753,7 @@ export function MovimentosPage() {
|
|||
|
||||
return (
|
||||
<TableRow
|
||||
key={item.idcontasapagar}
|
||||
key={`${item.idcontasapagar}:${item.ocorrencia_extrato || 'normal'}:${getDataPrincipal(item, dataCampo) || ''}`}
|
||||
hover
|
||||
sx={{
|
||||
backgroundColor: rowVisual.backgroundColor,
|
||||
|
|
@ -1762,6 +1788,19 @@ export function MovimentosPage() {
|
|||
</Tooltip>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
{dataCampo === 'extrato' && ocorrenciaExtratoLabel(item) ? (
|
||||
<Chip
|
||||
label={ocorrenciaExtratoLabel(item)}
|
||||
size="small"
|
||||
color={item.ocorrencia_extrato === 'baixa' ? 'success' : 'info'}
|
||||
variant="outlined"
|
||||
/>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Box sx={{ maxWidth: 260 }}>
|
||||
<Typography fontWeight={800} noWrap title={item.descricao}>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,15 @@ export type ListarMovimentosParams = {
|
|||
orderDirection?: OrderDirection;
|
||||
};
|
||||
|
||||
export type DataCampo =
|
||||
| 'extrato'
|
||||
| 'dataentrada'
|
||||
| 'datavencimento'
|
||||
| 'databaixa'
|
||||
| 'insert_date'
|
||||
| 'update_date'
|
||||
| 'deleted_at';
|
||||
|
||||
export async function listarMovimentos(
|
||||
params?: ListarMovimentosParams
|
||||
): Promise<MovimentosListResponse> {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export type Movimento = {
|
|||
dataentrada: string | null;
|
||||
datavencimento: string | null;
|
||||
databaixa: string | null;
|
||||
data_extrato?: string | null;
|
||||
valor: number;
|
||||
parcela: number;
|
||||
parcelas: number;
|
||||
|
|
@ -41,6 +42,7 @@ export type Movimento = {
|
|||
banco_referencia_descricao?: string | null;
|
||||
banco_referencia_debito?: number | null;
|
||||
movimento_fixo_descricao?: string | null;
|
||||
ocorrencia_extrato?: 'lancamento' | 'baixa' | null;
|
||||
};
|
||||
|
||||
export type CriarMovimentoRequest = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue