ajustado movimentos fixos sangria

This commit is contained in:
Diego Freitas 2026-06-09 21:45:46 -03:00
parent 12dcc8506f
commit 36aba1c646
8 changed files with 826 additions and 208 deletions

View File

@ -69,6 +69,18 @@ function validarDadosMovimentoFixo(dados) {
} }
} }
if (
dados.idbancos_p !== undefined &&
dados.idbancos_p !== null &&
dados.idbancos_p !== ''
) {
const idbancosP = Number(dados.idbancos_p);
if (!Number.isInteger(idbancosP) || idbancosP <= 0) {
return 'Banco de destino inválido.';
}
}
if (dados.habilitado !== undefined && dados.habilitado !== null && dados.habilitado !== '') { if (dados.habilitado !== undefined && dados.habilitado !== null && dados.habilitado !== '') {
const habilitado = Number(dados.habilitado); const habilitado = Number(dados.habilitado);
@ -77,6 +89,41 @@ function validarDadosMovimentoFixo(dados) {
} }
} }
if (
dados.gerar_automatico !== undefined &&
dados.gerar_automatico !== null &&
dados.gerar_automatico !== ''
) {
const gerarAutomatico =
Number(dados.gerar_automatico);
if (![0, 1].includes(gerarAutomatico)) {
return 'Configuração de geração automática inválida.';
}
}
if (dados.movimento === 'Sangria') {
const idBancoOrigem = Number(dados.idbancos);
const idBancoDestino = Number(dados.idbancos_p);
if (!idBancoDestino) {
return 'Banco de destino é obrigatório para sangria.';
}
if (idBancoOrigem === idBancoDestino) {
return 'Banco de origem e banco de destino não podem ser iguais.';
}
}
if (
dados.movimento !== 'Sangria' &&
dados.idbancos_p !== undefined &&
dados.idbancos_p !== null &&
dados.idbancos_p !== ''
) {
return 'Banco de destino somente pode ser informado em uma sangria.';
}
return null; return null;
} }
@ -84,22 +131,24 @@ async function listar(req, res) {
try { try {
const idUsuario = extrairIdUsuarioLogado(req); const idUsuario = extrairIdUsuarioLogado(req);
const resultado = await movimentosFixosService.listarMovimentosFixos({ const resultado =
limite: req.query.limite, await movimentosFixosService.listarMovimentosFixos({
offset: req.query.offset, limite: req.query.limite,
page: req.query.page, offset: req.query.offset,
busca: req.query.busca, page: req.query.page,
movimento: req.query.movimento, busca: req.query.busca,
idbancos: req.query.idbancos, movimento: req.query.movimento,
idcentrodecustos: req.query.idcentrodecustos, idbancos: req.query.idbancos,
idclientes: req.query.idclientes, idcentrodecustos: req.query.idcentrodecustos,
habilitado: req.query.habilitado, idclientes: req.query.idclientes,
diaInicio: req.query.diaInicio, habilitado: req.query.habilitado,
diaFim: req.query.diaFim, gerar_automatico: req.query.gerar_automatico,
orderBy: req.query.orderBy, diaInicio: req.query.diaInicio,
orderDirection: req.query.orderDirection, diaFim: req.query.diaFim,
idUsuario, orderBy: req.query.orderBy,
}); orderDirection: req.query.orderDirection,
idUsuario,
});
return res.json({ return res.json({
ok: true, ok: true,
@ -176,9 +225,12 @@ async function criar(req, res) {
} catch (error) { } catch (error) {
console.error('Erro ao criar movimento fixo:', error); console.error('Erro ao criar movimento fixo:', error);
return res.status(500).json({ return res.status(error.statusCode || 500).json({
ok: false, ok: false,
message: 'Erro ao criar movimento fixo.', message:
error.statusCode
? error.message
: 'Erro ao criar movimento fixo.',
}); });
} }
} }
@ -222,9 +274,12 @@ async function atualizar(req, res) {
} catch (error) { } catch (error) {
console.error('Erro ao atualizar movimento fixo:', error); console.error('Erro ao atualizar movimento fixo:', error);
return res.status(500).json({ return res.status(error.statusCode || 500).json({
ok: false, ok: false,
message: 'Erro ao atualizar movimento fixo.', message:
error.statusCode
? error.message
: 'Erro ao atualizar movimento fixo.',
}); });
} }
} }

View File

@ -9,19 +9,31 @@ const CAMPOS_MOVIMENTO_FIXO_SELECT = `
mf.idcentrodecustos, mf.idcentrodecustos,
mf.idclientes, mf.idclientes,
mf.habilitado, mf.habilitado,
mf.gerar_automatico,
mf.ultimo_mes_gerado,
mf.idbancos, mf.idbancos,
mf.idbancos_p,
mf.insert_date, mf.insert_date,
mf.update_date, mf.update_date,
b.descricao AS banco_descricao, b.descricao AS banco_descricao,
bp.descricao AS banco_destino_descricao,
cc.descricao AS centro_custo_descricao, cc.descricao AS centro_custo_descricao,
c.nome AS cliente_nome c.nome AS cliente_nome
`; `;
const FROM_MOVIMENTO_FIXO_JOIN = ` const FROM_MOVIMENTO_FIXO_JOIN = `
FROM movimentosfixos mf FROM movimentosfixos mf
INNER JOIN bancos b ON b.idbancos = mf.idbancos INNER JOIN bancos b
LEFT JOIN centrodecustos cc ON cc.idcentrodecustos = mf.idcentrodecustos ON b.idbancos = mf.idbancos
LEFT JOIN clientes c ON c.idclientes = mf.idclientes
LEFT JOIN bancos bp
ON bp.idbancos = mf.idbancos_p
LEFT JOIN centrodecustos cc
ON cc.idcentrodecustos = mf.idcentrodecustos
LEFT JOIN clientes c
ON c.idclientes = mf.idclientes
`; `;
function normalizarTextoOuNull(valor) { function normalizarTextoOuNull(valor) {
@ -96,10 +108,14 @@ function resolverOrdenacao(orderBy) {
idcentrodecustos: 'mf.idcentrodecustos', idcentrodecustos: 'mf.idcentrodecustos',
idclientes: 'mf.idclientes', idclientes: 'mf.idclientes',
idbancos: 'mf.idbancos', idbancos: 'mf.idbancos',
idbancos_p: 'mf.idbancos_p',
habilitado: 'mf.habilitado', habilitado: 'mf.habilitado',
gerar_automatico: 'mf.gerar_automatico',
ultimo_mes_gerado: 'mf.ultimo_mes_gerado',
insert_date: 'mf.insert_date', insert_date: 'mf.insert_date',
update_date: 'mf.update_date', update_date: 'mf.update_date',
banco_descricao: 'b.descricao', banco_descricao: 'b.descricao',
banco_destino_descricao: 'bp.descricao',
centro_custo_descricao: 'cc.descricao', centro_custo_descricao: 'cc.descricao',
cliente_nome: 'c.nome', cliente_nome: 'c.nome',
}; };
@ -145,6 +161,15 @@ function montarWhereMovimentosFixos(filtros = {}) {
params.push(Number(filtros.habilitado)); params.push(Number(filtros.habilitado));
} }
if (
filtros.gerar_automatico !== undefined &&
filtros.gerar_automatico !== null &&
filtros.gerar_automatico !== ''
) {
where.push('mf.gerar_automatico = ?');
params.push(Number(filtros.gerar_automatico));
}
if (filtros.diaInicio) { if (filtros.diaInicio) {
where.push('mf.dia_vencimento >= ?'); where.push('mf.dia_vencimento >= ?');
params.push(Number(filtros.diaInicio)); params.push(Number(filtros.diaInicio));
@ -161,6 +186,7 @@ function montarWhereMovimentosFixos(filtros = {}) {
mf.descricao LIKE ? mf.descricao LIKE ?
OR mf.movimento LIKE ? OR mf.movimento LIKE ?
OR b.descricao LIKE ? OR b.descricao LIKE ?
OR bp.descricao LIKE ?
OR cc.descricao LIKE ? OR cc.descricao LIKE ?
OR c.nome LIKE ? OR c.nome LIKE ?
) )
@ -173,6 +199,7 @@ function montarWhereMovimentosFixos(filtros = {}) {
termo, termo,
termo, termo,
termo, termo,
termo,
termo termo
); );
} }
@ -203,6 +230,110 @@ async function bancoPertenceAoUsuario(idbancos, idUsuario) {
return rows.length > 0; return rows.length > 0;
} }
async function bancoExiste(idbancos) {
if (!idbancos) return false;
const [rows] = await pool.query(
`
SELECT idbancos
FROM bancos
WHERE idbancos = ?
AND habilitado = 1
LIMIT 1
`,
[Number(idbancos)]
);
return rows.length > 0;
}
async function normalizarReferenciasMovimentoFixo(
dados,
idUsuario
) {
const movimento =
normalizarTextoOuNull(dados.movimento);
const idBanco =
normalizarNumeroOuNull(dados.idbancos);
const idBancoDestinoRecebido =
normalizarNumeroOuNull(dados.idbancos_p);
const idClienteRecebido =
normalizarNumeroOuNull(dados.idclientes);
const isSangria = movimento === 'Sangria';
if (!idBanco) {
const error = new Error(
'Banco/carteira é obrigatório para movimento fixo.'
);
error.statusCode = 400;
throw error;
}
const bancoPrincipalValido =
await bancoPertenceAoUsuario(
idBanco,
idUsuario
);
if (!bancoPrincipalValido) {
const error = new Error(
'Banco/carteira não pertence ao usuário logado.'
);
error.statusCode = 403;
throw error;
}
if (isSangria) {
if (!idBancoDestinoRecebido) {
const error = new Error(
'Banco de destino é obrigatório para sangria.'
);
error.statusCode = 400;
throw error;
}
if (idBanco === idBancoDestinoRecebido) {
const error = new Error(
'Banco de origem e banco de destino não podem ser iguais.'
);
error.statusCode = 400;
throw error;
}
const bancoDestinoValido =
await bancoExiste(idBancoDestinoRecebido);
if (!bancoDestinoValido) {
const error = new Error(
'Banco de destino inválido ou desabilitado.'
);
error.statusCode = 400;
throw error;
}
return {
idbancos: idBanco,
idclientes: null,
idbancos_p: idBancoDestinoRecebido,
};
}
return {
idbancos: idBanco,
idclientes: idClienteRecebido,
idbancos_p: null,
};
}
async function listarMovimentosFixos(filtros = {}) { async function listarMovimentosFixos(filtros = {}) {
const limite = limitarNumero(filtros.limite, 20, 1, 100); const limite = limitarNumero(filtros.limite, 20, 1, 100);
const page = limitarNumero(filtros.page, 1, 1, 999999); const page = limitarNumero(filtros.page, 1, 1, 999999);
@ -241,10 +372,10 @@ async function listarMovimentosFixos(filtros = {}) {
SELECT SELECT
COUNT(*) AS quantidade, COUNT(*) AS quantidade,
COALESCE(SUM(mf.valor), 0) AS valorTotal, COALESCE(SUM(mf.valor), 0) AS valorTotal,
COALESCE(SUM(CASE WHEN mf.movimento = 'Entrada' THEN mf.valor ELSE 0 END), 0) AS totalEntradas, COALESCE(SUM(CASE WHEN mf.movimento = 'Entrada' and mf.habilitado = 1 THEN mf.valor ELSE 0 END), 0) AS totalEntradas,
COALESCE(SUM(CASE WHEN mf.movimento = 'Saida' THEN mf.valor ELSE 0 END), 0) AS totalSaidas, COALESCE(SUM(CASE WHEN mf.movimento = 'Saida' and mf.habilitado = 1 THEN mf.valor ELSE 0 END), 0) AS totalSaidas,
COALESCE(SUM(CASE WHEN mf.movimento = 'Sangria' THEN mf.valor ELSE 0 END), 0) AS totalSangrias, COALESCE(SUM(CASE WHEN mf.movimento = 'Sangria' and mf.habilitado = 1 THEN mf.valor ELSE 0 END), 0) AS totalSangrias,
COALESCE(SUM(CASE WHEN mf.movimento = 'Estorno' THEN mf.valor ELSE 0 END), 0) AS totalEstornos, COALESCE(SUM(CASE WHEN mf.movimento = 'Estorno' and mf.habilitado = 1 THEN mf.valor ELSE 0 END), 0) AS totalEstornos,
COALESCE(SUM(CASE WHEN mf.habilitado = 1 THEN 1 ELSE 0 END), 0) AS habilitados, COALESCE(SUM(CASE WHEN mf.habilitado = 1 THEN 1 ELSE 0 END), 0) AS habilitados,
COALESCE(SUM(CASE WHEN mf.habilitado = 0 THEN 1 ELSE 0 END), 0) AS desabilitados COALESCE(SUM(CASE WHEN mf.habilitado = 0 THEN 1 ELSE 0 END), 0) AS desabilitados
${FROM_MOVIMENTO_FIXO_JOIN} ${FROM_MOVIMENTO_FIXO_JOIN}
@ -309,26 +440,34 @@ async function criarMovimentoFixo(dados, idUsuario) {
valor, valor,
dia_vencimento, dia_vencimento,
idcentrodecustos, idcentrodecustos,
idclientes,
habilitado, habilitado,
idbancos, gerar_automatico,
} = dados; } = dados;
const idBancoNormalizado = normalizarNumeroOuNull(idbancos); const movimentoNormalizado =
normalizarTextoOuNull(movimento);
const movimentosPermitidos = [
'Entrada',
'Saida',
'Sangria',
'Estorno',
];
if (!movimentosPermitidos.includes(movimentoNormalizado)) {
const error = new Error(
'Tipo de movimento fixo inválido.'
);
if (!idBancoNormalizado) {
const error = new Error('Banco/carteira é obrigatório para movimento fixo.');
error.statusCode = 400; error.statusCode = 400;
throw error; throw error;
} }
const bancoValido = await bancoPertenceAoUsuario(idBancoNormalizado, idUsuario); const referencias =
await normalizarReferenciasMovimentoFixo(
if (!bancoValido) { dados,
const error = new Error('Banco/carteira não pertence ao usuário logado.'); idUsuario
error.statusCode = 403; );
throw error;
}
const [result] = await pool.query( const [result] = await pool.query(
` `
@ -340,28 +479,46 @@ async function criarMovimentoFixo(dados, idUsuario) {
idcentrodecustos, idcentrodecustos,
idclientes, idclientes,
habilitado, habilitado,
gerar_automatico,
ultimo_mes_gerado,
idbancos, idbancos,
idbancos_p,
insert_date, insert_date,
update_date update_date
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW()) ) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, NOW(), NOW()
)
`, `,
[ [
normalizarTextoOuNull(movimento), movimentoNormalizado,
normalizarTextoOuNull(descricao), normalizarTextoOuNull(descricao),
normalizarNumero(valor, 0), normalizarNumero(valor, 0),
normalizarNumero(dia_vencimento, 1), normalizarNumero(dia_vencimento, 1),
normalizarNumeroOuNull(idcentrodecustos), normalizarNumeroOuNull(idcentrodecustos),
normalizarNumeroOuNull(idclientes), referencias.idclientes,
normalizarFlag(habilitado, 1), normalizarFlag(habilitado, 1),
idBancoNormalizado, normalizarFlag(gerar_automatico, 1),
referencias.idbancos,
referencias.idbancos_p,
] ]
); );
return buscarMovimentoFixoPorId(result.insertId, idUsuario); return buscarMovimentoFixoPorId(
result.insertId,
idUsuario
);
} }
async function atualizarMovimentoFixo(id, dados, idUsuario) { async function atualizarMovimentoFixo(
const movimentoAtual = await buscarMovimentoFixoPorId(id, idUsuario); id,
dados,
idUsuario
) {
const movimentoAtual =
await buscarMovimentoFixoPorId(
id,
idUsuario
);
if (!movimentoAtual) { if (!movimentoAtual) {
return null; return null;
@ -373,26 +530,46 @@ async function atualizarMovimentoFixo(id, dados, idUsuario) {
valor, valor,
dia_vencimento, dia_vencimento,
idcentrodecustos, idcentrodecustos,
idclientes,
habilitado, habilitado,
idbancos, gerar_automatico,
} = dados; } = dados;
const idBancoNormalizado = normalizarNumeroOuNull(idbancos); const movimentoNormalizado =
normalizarTextoOuNull(movimento);
const movimentosPermitidos = [
'Entrada',
'Saida',
'Sangria',
'Estorno',
];
if (!movimentosPermitidos.includes(movimentoNormalizado)) {
const error = new Error(
'Tipo de movimento fixo inválido.'
);
if (!idBancoNormalizado) {
const error = new Error('Banco/carteira é obrigatório para movimento fixo.');
error.statusCode = 400; error.statusCode = 400;
throw error; throw error;
} }
const bancoValido = await bancoPertenceAoUsuario(idBancoNormalizado, idUsuario); const referencias =
await normalizarReferenciasMovimentoFixo(
dados,
idUsuario
);
if (!bancoValido) { const gerarAutomaticoNormalizado =
const error = new Error('Banco/carteira não pertence ao usuário logado.'); gerar_automatico === undefined ||
error.statusCode = 403; gerar_automatico === null ||
throw error; gerar_automatico === ''
} ? Number(
movimentoAtual.gerar_automatico ?? 1
)
: normalizarFlag(
gerar_automatico,
1
);
await pool.query( await pool.query(
` `
@ -405,24 +582,31 @@ async function atualizarMovimentoFixo(id, dados, idUsuario) {
idcentrodecustos = ?, idcentrodecustos = ?,
idclientes = ?, idclientes = ?,
habilitado = ?, habilitado = ?,
gerar_automatico = ?,
idbancos = ?, idbancos = ?,
idbancos_p = ?,
update_date = NOW() update_date = NOW()
WHERE idmovimentosfixos = ? WHERE idmovimentosfixos = ?
`, `,
[ [
normalizarTextoOuNull(movimento), movimentoNormalizado,
normalizarTextoOuNull(descricao), normalizarTextoOuNull(descricao),
normalizarNumero(valor, 0), normalizarNumero(valor, 0),
normalizarNumero(dia_vencimento, 1), normalizarNumero(dia_vencimento, 1),
normalizarNumeroOuNull(idcentrodecustos), normalizarNumeroOuNull(idcentrodecustos),
normalizarNumeroOuNull(idclientes), referencias.idclientes,
normalizarFlag(habilitado, 1), normalizarFlag(habilitado, 1),
idBancoNormalizado, gerarAutomaticoNormalizado,
referencias.idbancos,
referencias.idbancos_p,
id, id,
] ]
); );
return buscarMovimentoFixoPorId(id, idUsuario); return buscarMovimentoFixoPorId(
id,
idUsuario
);
} }
async function alterarHabilitadoMovimentoFixo(id, habilitado, idUsuario) { async function alterarHabilitadoMovimentoFixo(id, habilitado, idUsuario) {

View File

@ -85,26 +85,21 @@ function statusInicialPorMovimento(movimento) {
return 'A receber'; return 'A receber';
} }
if (movimento === 'Saida') { if (movimento === 'Saida' || movimento === 'Sangria') {
return 'A pagar'; return 'A pagar';
} }
if (movimento === 'Sangria') {
return 'Pago';
}
return 'A pagar'; return 'A pagar';
} }
function dataBaixaInicialPorMovimento(movimento, dataVencimento) { function dataBaixaInicialPorMovimento() {
if (movimento === 'Sangria') {
return dataVencimento;
}
return null; return null;
} }
async function buscarFixosDoUsuario(connection, idUsuario) { async function buscarFixosDoUsuario(
connection,
idUsuario
) {
const [rows] = await connection.query( const [rows] = await connection.query(
` `
SELECT SELECT
@ -115,11 +110,19 @@ async function buscarFixosDoUsuario(connection, idUsuario) {
mf.dia_vencimento, mf.dia_vencimento,
mf.idcentrodecustos, mf.idcentrodecustos,
mf.idclientes, mf.idclientes,
mf.idbancos mf.idbancos,
mf.idbancos_p
FROM movimentosfixos mf FROM movimentosfixos mf
INNER JOIN bancos b ON b.idbancos = mf.idbancos INNER JOIN bancos b
ON b.idbancos = mf.idbancos
WHERE mf.habilitado = 1 WHERE mf.habilitado = 1
AND mf.gerar_automatico = 1 AND mf.gerar_automatico = 1
AND mf.movimento IN (
'Entrada',
'Saida',
'Sangria',
'Estorno'
)
AND b.idusuarios = ? AND b.idusuarios = ?
ORDER BY mf.idmovimentosfixos ASC ORDER BY mf.idmovimentosfixos ASC
`, `,
@ -183,35 +186,53 @@ async function jaExisteMovimentoFixo(connection, fixo, competencia) {
return true; return true;
} }
const [rowsMovimentoLegado] = await connection.query( const [rowsMovimentoLegado] =
` await connection.query(
SELECT idcontasapagar `
FROM contasapagar SELECT idcontasapagar
WHERE deleted_at IS NULL FROM contasapagar
AND idmovimentosfixos IS NULL WHERE deleted_at IS NULL
AND competencia IS NULL AND idmovimentosfixos IS NULL
AND movimento = ? AND competencia IS NULL
AND descricao = ? AND movimento = ?
AND idbancos = ? AND descricao = ?
AND ( AND idbancos = ?
(? IS NULL AND idcentrodecustos IS NULL) AND (
OR idcentrodecustos = ? (? IS NULL AND idcentrodecustos IS NULL)
) OR idcentrodecustos = ?
AND DATE(datavencimento) BETWEEN ? AND ? )
ORDER BY idcontasapagar DESC AND (
LIMIT 1 (? IS NULL AND idclientes IS NULL)
FOR UPDATE OR idclientes = ?
`, )
[ AND (
fixo.movimento, (? IS NULL AND idbancos_p IS NULL)
fixo.descricao, OR idbancos_p = ?
fixo.idbancos, )
fixo.idcentrodecustos || null, AND DATE(datavencimento)
fixo.idcentrodecustos || null, BETWEEN ? AND ?
`${competencia}-01`, ORDER BY idcontasapagar DESC
`${competencia}-31`, LIMIT 1
] FOR UPDATE
); `,
[
fixo.movimento,
fixo.descricao,
fixo.idbancos,
fixo.idcentrodecustos || null,
fixo.idcentrodecustos || null,
fixo.idclientes || null,
fixo.idclientes || null,
fixo.idbancos_p || null,
fixo.idbancos_p || null,
`${competencia}-01`,
`${competencia}-31`,
]
);
if (rowsMovimentoLegado.length > 0) { if (rowsMovimentoLegado.length > 0) {
const idContasPagar = rowsMovimentoLegado[0].idcontasapagar; const idContasPagar = rowsMovimentoLegado[0].idcontasapagar;
@ -289,7 +310,20 @@ async function inserirMovimentoFixoGerado(connection, fixo, competencia, idUsuar
insert_date, insert_date,
update_date, update_date,
deleted_at deleted_at
) VALUES (?, ?, ?, ?, ?, ?, 1, 1, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?, NULL, 'movimento_fixo', 0, NULL, NOW(), NOW(), NULL) ) VALUES (
?, ?, ?, ?, ?, ?,
1, 1,
?, ?, ?, ?, ?, ?,
NULL,
?, ?, ?,
NULL,
'movimento_fixo',
0,
NULL,
NOW(),
NOW(),
NULL
)
`, `,
[ [
fixo.movimento, fixo.movimento,
@ -303,7 +337,15 @@ async function inserirMovimentoFixoGerado(connection, fixo, competencia, idUsuar
fixo.idbancos || null, fixo.idbancos || null,
idUsuario, idUsuario,
dataBaixa ? idUsuario : null, dataBaixa ? idUsuario : null,
fixo.idclientes || null,
fixo.movimento === 'Sangria'
? null
: fixo.idclientes || null,
fixo.movimento === 'Sangria'
? fixo.idbancos_p || null
: null,
fixo.idmovimentosfixos, fixo.idmovimentosfixos,
competencia, competencia,
] ]

View File

@ -26,6 +26,19 @@ function pad2(valor) {
return String(valor).padStart(2, '0'); return String(valor).padStart(2, '0');
} }
function resolverInvestimentoMovimento(movimento) {
const centroEhInvestimento =
Number(movimento.centro_custo_investimento) === 1;
const destinoEhInvestimento =
movimento.movimento === 'Sangria' &&
Number(movimento.banco_referencia_investimento) === 1;
return centroEhInvestimento || destinoEhInvestimento
? 1
: 0;
}
function ultimoDiaDoMes(ano, mes) { function ultimoDiaDoMes(ano, mes) {
return new Date(Number(ano), Number(mes), 0).getDate(); return new Date(Number(ano), Number(mes), 0).getDate();
} }
@ -218,7 +231,8 @@ async function buscarMovimentosDoMes(connection, { idUsuario, dataInicio, dataFi
cc.descricao AS centro_custo_descricao, cc.descricao AS centro_custo_descricao,
cc.investimento AS centro_custo_investimento, cc.investimento AS centro_custo_investimento,
c.nome AS cliente_nome, c.nome AS cliente_nome,
bp.descricao AS banco_referencia_descricao bp.descricao AS banco_referencia_descricao,
bp.investimento AS banco_referencia_investimento
FROM contasapagar cp FROM contasapagar cp
INNER JOIN bancos b INNER JOIN bancos b
ON b.idbancos = cp.idbancos ON b.idbancos = cp.idbancos
@ -287,14 +301,23 @@ async function buscarFixosUsuario(connection, idUsuario) {
mf.idcentrodecustos, mf.idcentrodecustos,
mf.idclientes, mf.idclientes,
mf.idbancos, mf.idbancos,
mf.idbancos_p,
b.descricao AS banco_descricao, b.descricao AS banco_descricao,
b.debito AS banco_debito, b.debito AS banco_debito,
bp.descricao AS banco_referencia_descricao,
bp.investimento AS banco_referencia_investimento,
cc.descricao AS centro_custo_descricao, cc.descricao AS centro_custo_descricao,
cc.investimento AS centro_custo_investimento, cc.investimento AS centro_custo_investimento,
c.nome AS cliente_nome c.nome AS cliente_nome
FROM movimentosfixos mf FROM movimentosfixos mf
INNER JOIN bancos b INNER JOIN bancos b
ON b.idbancos = mf.idbancos ON b.idbancos = mf.idbancos
LEFT JOIN bancos bp
ON bp.idbancos = mf.idbancos_p
LEFT JOIN centrodecustos cc LEFT JOIN centrodecustos cc
ON cc.idcentrodecustos = mf.idcentrodecustos ON cc.idcentrodecustos = mf.idcentrodecustos
LEFT JOIN clientes c LEFT JOIN clientes c
@ -302,7 +325,9 @@ async function buscarFixosUsuario(connection, idUsuario) {
WHERE mf.habilitado = 1 WHERE mf.habilitado = 1
AND b.idusuarios = ? AND b.idusuarios = ?
AND COALESCE(mf.gerar_automatico, 1) = 1 AND COALESCE(mf.gerar_automatico, 1) = 1
ORDER BY mf.dia_vencimento ASC, mf.descricao ASC ORDER BY
mf.dia_vencimento ASC,
mf.descricao ASC
`, `,
[idUsuario] [idUsuario]
); );
@ -363,8 +388,14 @@ function montarItensMovimentosExistentes(movimentosDoMes, incluirQuitadas) {
const saidas = []; const saidas = [];
for (const movimento of movimentosDoMes) { for (const movimento of movimentosDoMes) {
const bancoDebito = Number(movimento.banco_debito) === 1; const bancoDebito =
const bancoCredito = Number(movimento.banco_debito) === 0; Number(movimento.banco_debito) === 1;
const bancoCredito =
Number(movimento.banco_debito) === 0;
const investimento =
resolverInvestimentoMovimento(movimento);
if (bancoDebito) { if (bancoDebito) {
if (!incluirQuitadas && !statusEmAberto(movimento.status)) { if (!incluirQuitadas && !statusEmAberto(movimento.status)) {
@ -387,7 +418,7 @@ function montarItensMovimentosExistentes(movimentosDoMes, incluirQuitadas) {
banco_debito: movimento.banco_debito, banco_debito: movimento.banco_debito,
idcentrodecustos: movimento.idcentrodecustos, idcentrodecustos: movimento.idcentrodecustos,
centro_custo_descricao: movimento.centro_custo_descricao, centro_custo_descricao: movimento.centro_custo_descricao,
investimento: movimento.centro_custo_investimento, investimento: investimento,
idclientes: movimento.idclientes, idclientes: movimento.idclientes,
cliente_nome: movimento.cliente_nome, cliente_nome: movimento.cliente_nome,
detalhes: { detalhes: {
@ -415,7 +446,7 @@ function montarItensMovimentosExistentes(movimentosDoMes, incluirQuitadas) {
banco_debito: movimento.banco_debito, banco_debito: movimento.banco_debito,
idcentrodecustos: movimento.idcentrodecustos, idcentrodecustos: movimento.idcentrodecustos,
centro_custo_descricao: movimento.centro_custo_descricao, centro_custo_descricao: movimento.centro_custo_descricao,
investimento: movimento.centro_custo_investimento, investimento: investimento,
idclientes: movimento.idclientes, idclientes: movimento.idclientes,
cliente_nome: movimento.cliente_nome, cliente_nome: movimento.cliente_nome,
detalhes: { detalhes: {
@ -443,7 +474,7 @@ function montarItensMovimentosExistentes(movimentosDoMes, incluirQuitadas) {
banco_debito: movimento.banco_debito, banco_debito: movimento.banco_debito,
idcentrodecustos: movimento.idcentrodecustos, idcentrodecustos: movimento.idcentrodecustos,
centro_custo_descricao: movimento.centro_custo_descricao, centro_custo_descricao: movimento.centro_custo_descricao,
investimento: movimento.centro_custo_investimento, investimento: investimento,
idclientes: movimento.idclientes, idclientes: movimento.idclientes,
cliente_nome: movimento.cliente_nome, cliente_nome: movimento.cliente_nome,
}) })
@ -553,6 +584,7 @@ function montarItensFixosPrevistos({
} }
const dataVencimento = montarDataVencimento(ano, mes, fixo.dia_vencimento); const dataVencimento = montarDataVencimento(ano, mes, fixo.dia_vencimento);
const investimento = resolverInvestimentoMovimento(fixo);
if (movimentoEhEntrada(fixo.movimento)) { if (movimentoEhEntrada(fixo.movimento)) {
entradas.push( entradas.push(
@ -570,7 +602,7 @@ function montarItensFixosPrevistos({
banco_debito: fixo.banco_debito, banco_debito: fixo.banco_debito,
idcentrodecustos: fixo.idcentrodecustos, idcentrodecustos: fixo.idcentrodecustos,
centro_custo_descricao: fixo.centro_custo_descricao, centro_custo_descricao: fixo.centro_custo_descricao,
investimento: fixo.centro_custo_investimento, investimento: investimento,
idclientes: fixo.idclientes, idclientes: fixo.idclientes,
cliente_nome: fixo.cliente_nome, cliente_nome: fixo.cliente_nome,
}) })
@ -585,15 +617,16 @@ function montarItensFixosPrevistos({
descricao: fixo.descricao, descricao: fixo.descricao,
valor: fixo.valor, valor: fixo.valor,
movimento: fixo.movimento, movimento: fixo.movimento,
status: fixo.movimento === 'Sangria' ? 'Pago' : 'A pagar', status: 'A pagar',
data: dataVencimento, data: dataVencimento,
idmovimentosfixos: fixo.idmovimentosfixos, idmovimentosfixos: fixo.idmovimentosfixos,
idbancos: fixo.idbancos, idbancos: fixo.idbancos,
banco_descricao: fixo.banco_descricao, banco_descricao: fixo.banco_descricao,
banco_debito: fixo.banco_debito, banco_debito: fixo.banco_debito,
idcentrodecustos: fixo.idcentrodecustos, idcentrodecustos: fixo.idcentrodecustos,
centro_custo_descricao: fixo.centro_custo_descricao, centro_custo_descricao:
investimento: fixo.centro_custo_investimento, fixo.centro_custo_descricao,
investimento,
idclientes: fixo.idclientes, idclientes: fixo.idclientes,
cliente_nome: fixo.cliente_nome, cliente_nome: fixo.cliente_nome,
}) })

View File

@ -283,9 +283,13 @@ export function MovimentoForm({ mode, initialData }: MovimentoFormProps) {
}, [meusBancos, idBanco]); }, [meusBancos, idBanco]);
const statusDisponiveis = useMemo<MovimentoStatus[]>(() => { const statusDisponiveis = useMemo<MovimentoStatus[]>(() => {
if (movimento === 'Entrada') return ['Recebido', 'A receber']; if (movimento === 'Entrada') {
if (movimento === 'Saida') return ['Pago', 'A pagar']; return ['Recebido', 'A receber'];
if (movimento === 'Sangria') return ['Pago']; }
if (movimento === 'Saida' || movimento === 'Sangria') {
return ['Pago', 'A pagar'];
}
return ['Pago', 'Recebido']; return ['Pago', 'Recebido'];
}, [movimento]); }, [movimento]);
@ -324,8 +328,6 @@ export function MovimentoForm({ mode, initialData }: MovimentoFormProps) {
if (movimento === 'Sangria') { if (movimento === 'Sangria') {
setReferenciaTipo('banco'); setReferenciaTipo('banco');
setStatus('Pago');
setDataBaixa((dataAtual) => dataAtual || hojeISO());
setParcelas('1'); setParcelas('1');
setParcela('1'); setParcela('1');
setGerarParcelas(false); setGerarParcelas(false);
@ -415,6 +417,17 @@ export function MovimentoForm({ mode, initialData }: MovimentoFormProps) {
status, status,
]); ]);
useEffect(() => {
if (status === 'A pagar' || status === 'A receber') {
setDataBaixa('');
return;
}
if (!dataBaixa) {
setDataBaixa(hojeISO());
}
}, [status]);
function limparFormulario() { function limparFormulario() {
setDescricao(''); setDescricao('');
setValor(''); setValor('');
@ -567,8 +580,10 @@ export function MovimentoForm({ mode, initialData }: MovimentoFormProps) {
function montarPayload(confirmarDuplicado = false): CriarMovimentoRequest { function montarPayload(confirmarDuplicado = false): CriarMovimentoRequest {
const isSangria = movimento === 'Sangria'; const isSangria = movimento === 'Sangria';
const statusFinal = isSangria ? 'Pago' : status; const statusFinal = status;
const referenciaTipoFinal = isSangria ? 'banco' : referenciaTipo; const referenciaTipoFinal = isSangria
? 'banco'
: referenciaTipo;
const parcelaFinal = isSangria ? 1 : Number(parcela || 1); const parcelaFinal = isSangria ? 1 : Number(parcela || 1);
const parcelasFinal = isSangria ? 1 : Number(parcelas || 1); const parcelasFinal = isSangria ? 1 : Number(parcelas || 1);
const dataBaixaFinal = const dataBaixaFinal =
@ -731,7 +746,6 @@ export function MovimentoForm({ mode, initialData }: MovimentoFormProps) {
label="Situação" label="Situação"
value={status} value={status}
onChange={(event) => setStatus(event.target.value as MovimentoStatus)} onChange={(event) => setStatus(event.target.value as MovimentoStatus)}
disabled={movimento === 'Sangria'}
fullWidth fullWidth
sx={fieldSx} sx={fieldSx}
> >

View File

@ -20,6 +20,7 @@ import EventRepeatIcon from '@mui/icons-material/EventRepeat';
import SaveIcon from '@mui/icons-material/Save'; import SaveIcon from '@mui/icons-material/Save';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {
listarBancos,
listarMeusBancos, listarMeusBancos,
listarCentrosCusto, listarCentrosCusto,
listarClientes, listarClientes,
@ -123,9 +124,8 @@ function movimentoLabel(movimento: MovimentoFixoTipo) {
export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps) { export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const isEdit = mode === 'edit'; const [meusBancos, setMeusBancos] = useState<Banco[]>([]);
const [todosBancos, setTodosBancos] = useState<Banco[]>([]);
const [bancos, setBancos] = useState<Banco[]>([]);
const [centrosCusto, setCentrosCusto] = useState<CentroCusto[]>([]); const [centrosCusto, setCentrosCusto] = useState<CentroCusto[]>([]);
const [clientes, setClientes] = useState<Cliente[]>([]); const [clientes, setClientes] = useState<Cliente[]>([]);
@ -141,7 +141,16 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
const [idCentroCusto, setIdCentroCusto] = useState(''); const [idCentroCusto, setIdCentroCusto] = useState('');
const [idCliente, setIdCliente] = useState(''); const [idCliente, setIdCliente] = useState('');
const [idBanco, setIdBanco] = useState(''); const [idBanco, setIdBanco] = useState('');
const [idBancoReferencia, setIdBancoReferencia] = useState('');
const [habilitado, setHabilitado] = useState('1'); const [habilitado, setHabilitado] = useState('1');
const [gerarAutomatico, setGerarAutomatico] = useState('1');
const isEdit = mode === 'edit';
const isSangria = movimento === 'Sangria';
const usaCliente =
movimento === 'Entrada' ||
movimento === 'Saida' ||
movimento === 'Estorno';
const titulo = isEdit ? 'Editar movimento fixo' : 'Novo movimento fixo'; const titulo = isEdit ? 'Editar movimento fixo' : 'Novo movimento fixo';
const subtitulo = isEdit const subtitulo = isEdit
@ -149,10 +158,19 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
: 'Cadastre lançamentos recorrentes para automatizar o financeiro depois.'; : 'Cadastre lançamentos recorrentes para automatizar o financeiro depois.';
const movimentoDescricao = useMemo(() => { const movimentoDescricao = useMemo(() => {
if (movimento === 'Entrada') return 'Entrada recorrente'; if (movimento === 'Entrada') {
if (movimento === 'Saida') return 'Saída recorrente'; return 'Receita recorrente a receber';
if (movimento === 'Sangria') return 'Sangria recorrente'; }
return 'Estorno recorrente';
if (movimento === 'Saida') {
return 'Despesa recorrente a pagar';
}
if (movimento === 'Sangria') {
return 'Retirada recorrente a efetivar';
}
return 'Estorno recorrente a receber';
}, [movimento]); }, [movimento]);
const clienteSelecionado = useMemo(() => { const clienteSelecionado = useMemo(() => {
@ -161,6 +179,28 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
return clientes.find((cliente) => String(cliente.id) === String(idCliente)) || null; return clientes.find((cliente) => String(cliente.id) === String(idCliente)) || null;
}, [clientes, idCliente]); }, [clientes, idCliente]);
const bancoOrigemSelecionado = useMemo(() => {
if (!idBanco) return null;
return (
meusBancos.find(
(banco) =>
String(banco.id) === String(idBanco)
) || null
);
}, [meusBancos, idBanco]);
const bancoDestinoSelecionado = useMemo(() => {
if (!idBancoReferencia) return null;
return (
todosBancos.find(
(banco) =>
String(banco.id) === String(idBancoReferencia)
) || null
);
}, [todosBancos, idBancoReferencia]);
useEffect(() => { useEffect(() => {
if (!initialData) return; if (!initialData) return;
@ -168,10 +208,38 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
setDescricao(initialData.descricao || ''); setDescricao(initialData.descricao || '');
setValor(String(initialData.valor || '')); setValor(String(initialData.valor || ''));
setDiaVencimento(String(initialData.dia_vencimento || 1)); setDiaVencimento(String(initialData.dia_vencimento || 1));
setIdCentroCusto(initialData.idcentrodecustos ? String(initialData.idcentrodecustos) : '');
setIdCliente(initialData.idclientes ? String(initialData.idclientes) : ''); setIdCentroCusto(
setIdBanco(initialData.idbancos ? String(initialData.idbancos) : ''); initialData.idcentrodecustos
setHabilitado(String(initialData.habilitado ?? 1)); ? String(initialData.idcentrodecustos)
: ''
);
setIdCliente(
initialData.idclientes
? String(initialData.idclientes)
: ''
);
setIdBanco(
initialData.idbancos
? String(initialData.idbancos)
: ''
);
setIdBancoReferencia(
initialData.idbancos_p
? String(initialData.idbancos_p)
: ''
);
setHabilitado(
String(initialData.habilitado ?? 1)
);
setGerarAutomatico(
String(initialData.gerar_automatico ?? 1)
);
}, [initialData]); }, [initialData]);
useEffect(() => { useEffect(() => {
@ -180,13 +248,20 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
setLoadingRefs(true); setLoadingRefs(true);
setErro(''); setErro('');
const [bancosData, centrosData, clientesData] = await Promise.all([ const [
meusBancosData,
todosBancosData,
centrosData,
clientesData,
] = await Promise.all([
listarMeusBancos(), listarMeusBancos(),
listarBancos(),
listarCentrosCusto(), listarCentrosCusto(),
listarClientes(), listarClientes(),
]); ]);
setBancos(bancosData); setMeusBancos(meusBancosData);
setTodosBancos(todosBancosData);
setCentrosCusto(centrosData); setCentrosCusto(centrosData);
setClientes(clientesData); setClientes(clientesData);
} catch (error: any) { } catch (error: any) {
@ -203,6 +278,17 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
carregarReferencias(); carregarReferencias();
}, []); }, []);
useEffect(() => {
if (movimento === 'Sangria') {
// Sangria usa banco de destino e nunca cliente.
setIdCliente('');
return;
}
// Outros movimentos usam cliente e nunca banco de destino.
setIdBancoReferencia('');
}, [movimento]);
function limparFormulario() { function limparFormulario() {
setMovimento('Saida'); setMovimento('Saida');
setDescricao(''); setDescricao('');
@ -211,7 +297,9 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
setIdCentroCusto(''); setIdCentroCusto('');
setIdCliente(''); setIdCliente('');
setIdBanco(''); setIdBanco('');
setIdBancoReferencia('');
setHabilitado('1'); setHabilitado('1');
setGerarAutomatico('1');
} }
async function handleSubmit(event: FormEvent<HTMLFormElement>) { async function handleSubmit(event: FormEvent<HTMLFormElement>) {
@ -234,6 +322,28 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
return; return;
} }
if (!idBanco) {
setErro('Selecione o banco ou carteira do movimento fixo.');
return;
}
if (usaCliente && !idCliente) {
setErro('Selecione o cliente de referência.');
return;
}
if (isSangria) {
if (!idBancoReferencia) {
setErro('Selecione o banco de destino da sangria.');
return;
}
if (String(idBanco) === String(idBancoReferencia)) {
setErro('Banco de origem e banco de destino não podem ser iguais.');
return;
}
}
try { try {
setSaving(true); setSaving(true);
setErro(''); setErro('');
@ -244,10 +354,23 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
descricao: descricao.trim(), descricao: descricao.trim(),
valor: Number(valor), valor: Number(valor),
dia_vencimento: dia, dia_vencimento: dia,
idcentrodecustos: toNumberOrNull(idCentroCusto), idcentrodecustos: toNumberOrNull(idCentroCusto),
idclientes: toNumberOrNull(idCliente),
habilitado: Number(habilitado || 1), idclientes:
idbancos: toNumberOrNull(idBanco), usaCliente
? toNumberOrNull(idCliente)
: null,
idbancos: Number(idBanco),
idbancos_p:
isSangria
? toNumberOrNull(idBancoReferencia)
: null,
habilitado: Number(habilitado),
gerar_automatico: Number(gerarAutomatico),
}; };
if (isEdit) { if (isEdit) {
@ -377,6 +500,30 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
<MenuItem value="0">Desabilitado</MenuItem> <MenuItem value="0">Desabilitado</MenuItem>
</TextField> </TextField>
<TextField
select
label="Geração automática"
value={gerarAutomatico}
onChange={(event) =>
setGerarAutomatico(event.target.value)
}
helperText={
Number(gerarAutomatico) === 1
? 'O movimento será gerado automaticamente em cada competência.'
: 'O cadastro ficará salvo, mas não será gerado automaticamente.'
}
fullWidth
sx={fieldSx}
>
<MenuItem value="1">
Gerar automaticamente
</MenuItem>
<MenuItem value="0">
Não gerar automaticamente
</MenuItem>
</TextField>
<TextField <TextField
label="Descrição" label="Descrição"
value={descricao} value={descricao}
@ -415,19 +562,31 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
<FormSection <FormSection
title="Classificação" title="Classificação"
description="Associe banco/carteira, centro de custo e cliente ao movimento fixo." description={
isSangria
? 'Associe o banco de origem, o banco de destino e o centro de custo.'
: 'Associe banco/carteira, centro de custo e cliente ao movimento fixo.'
}
> >
<TextField <TextField
select select
label="Banco/Carteira" required
label={isSangria ? 'Banco de origem' : 'Banco/Carteira'}
value={idBanco} value={idBanco}
onChange={(event) => setIdBanco(event.target.value)} onChange={(event) => setIdBanco(event.target.value)}
helperText={
isSangria
? 'Conta da qual o valor será retirado.'
: 'Conta ou carteira associada ao movimento.'
}
fullWidth fullWidth
sx={fieldSx} sx={fieldSx}
> >
<MenuItem value="">Nenhum</MenuItem> {meusBancos.map((banco) => (
{bancos.map((banco) => ( <MenuItem
<MenuItem key={banco.id} value={String(banco.id)}> key={banco.id}
value={String(banco.id)}
>
{banco.descricao} {banco.descricao}
</MenuItem> </MenuItem>
))} ))}
@ -437,33 +596,80 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
select select
label="Centro de custo" label="Centro de custo"
value={idCentroCusto} value={idCentroCusto}
onChange={(event) => setIdCentroCusto(event.target.value)} onChange={(event) =>
setIdCentroCusto(event.target.value)
}
fullWidth fullWidth
sx={fieldSx} sx={fieldSx}
> >
<MenuItem value="">Nenhum</MenuItem> <MenuItem value="">Nenhum</MenuItem>
{centrosCusto.map((centro) => ( {centrosCusto.map((centro) => (
<MenuItem key={centro.id} value={String(centro.id)}> <MenuItem
key={centro.id}
value={String(centro.id)}
>
{centro.descricao} {centro.descricao}
</MenuItem> </MenuItem>
))} ))}
</TextField> </TextField>
<TextField {usaCliente && (
select <TextField
label="Cliente" select
value={idCliente} required
onChange={(event) => setIdCliente(event.target.value)} label="Cliente"
fullWidth value={idCliente}
sx={fieldSx} onChange={(event) =>
> setIdCliente(event.target.value)
<MenuItem value="">Nenhum</MenuItem> }
{clientes.map((cliente) => ( helperText="Cliente relacionado ao movimento recorrente."
<MenuItem key={cliente.id} value={String(cliente.id)}> fullWidth
{cliente.nome} sx={fieldSx}
</MenuItem> >
))} <MenuItem value="">Selecione</MenuItem>
</TextField>
{clientes.map((cliente) => (
<MenuItem
key={cliente.id}
value={String(cliente.id)}
>
{cliente.nome}
</MenuItem>
))}
</TextField>
)}
{isSangria && (
<TextField
select
required
label="Banco de destino"
value={idBancoReferencia}
onChange={(event) =>
setIdBancoReferencia(event.target.value)
}
helperText="Conta que receberá o valor da sangria."
fullWidth
sx={fieldSx}
>
<MenuItem value="">Selecione</MenuItem>
{todosBancos
.filter(
(banco) =>
String(banco.id) !== String(idBanco)
)
.map((banco) => (
<MenuItem
key={banco.id}
value={String(banco.id)}
>
{banco.descricao}
</MenuItem>
))}
</TextField>
)}
</FormSection> </FormSection>
<Stack <Stack
@ -592,13 +798,60 @@ export function MovimentoFixoForm({ mode, initialData }: MovimentoFixoFormProps)
</Box> </Box>
<Box> <Box>
<Typography variant="caption" color="text.secondary"> <Typography
Cliente variant="caption"
color="text.secondary"
>
Geração
</Typography> </Typography>
<Typography fontWeight={800}>
{clienteSelecionado?.nome || 'Nenhum'} <Typography
fontWeight={800}
color={
Number(gerarAutomatico) === 1
? 'primary.main'
: 'text.secondary'
}
>
{Number(gerarAutomatico) === 1
? 'Automática'
: 'Manual'}
</Typography> </Typography>
</Box> </Box>
<Box>
<Typography variant="caption" color="text.secondary">
{isSangria ? 'Banco de origem' : 'Banco/Carteira'}
</Typography>
<Typography fontWeight={800}>
{bancoOrigemSelecionado?.descricao || 'Não selecionado'}
</Typography>
</Box>
{usaCliente && (
<Box>
<Typography variant="caption" color="text.secondary">
Cliente
</Typography>
<Typography fontWeight={800}>
{clienteSelecionado?.nome || 'Não selecionado'}
</Typography>
</Box>
)}
{isSangria && (
<Box>
<Typography variant="caption" color="text.secondary">
Banco de destino
</Typography>
<Typography fontWeight={800}>
{bancoDestinoSelecionado?.descricao || 'Não selecionado'}
</Typography>
</Box>
)}
</Stack> </Stack>
</Paper> </Paper>
</Stack> </Stack>

View File

@ -1,4 +1,8 @@
export type MovimentoFixoTipo = 'Entrada' | 'Saida' | 'Sangria' | 'Estorno'; export type MovimentoFixoTipo =
| 'Entrada'
| 'Saida'
| 'Sangria'
| 'Estorno';
export type MovimentoFixo = { export type MovimentoFixo = {
idmovimentosfixos: number; idmovimentosfixos: number;
@ -6,14 +10,25 @@ export type MovimentoFixo = {
descricao: string; descricao: string;
valor: number; valor: number;
dia_vencimento: number; dia_vencimento: number;
idcentrodecustos: number | null; idcentrodecustos: number | null;
idclientes?: number | null; idclientes: number | null;
// Banco principal/origem do movimento.
idbancos: number;
// Banco de destino utilizado apenas em Sangria.
idbancos_p: number | null;
habilitado: number; habilitado: number;
idbancos: number | null; gerar_automatico: number;
ultimo_mes_gerado: string | null;
insert_date: string | null; insert_date: string | null;
update_date: string | null; update_date: string | null;
banco_descricao?: string | null; banco_descricao?: string | null;
banco_destino_descricao?: string | null;
centro_custo_descricao?: string | null; centro_custo_descricao?: string | null;
cliente_nome?: string | null; cliente_nome?: string | null;
}; };
@ -23,42 +38,16 @@ export type CriarMovimentoFixoRequest = {
descricao: string; descricao: string;
valor: number; valor: number;
dia_vencimento: number; dia_vencimento: number;
idcentrodecustos: number | null; idcentrodecustos: number | null;
idclientes?: number | null; idclientes: number | null;
idbancos: number;
idbancos_p: number | null;
habilitado: number; habilitado: number;
idbancos: number | null; gerar_automatico: number;
}; };
export type AtualizarMovimentoFixoRequest = CriarMovimentoFixoRequest; export type AtualizarMovimentoFixoRequest =
CriarMovimentoFixoRequest;
export type MovimentosFixosPagination = {
total: number;
limite: number;
offset: number;
page: number;
totalPages: number;
};
export type MovimentosFixosSummary = {
quantidade: number;
valorTotal: number;
totalEntradas: number;
totalSaidas: number;
totalSangrias: number;
totalEstornos: number;
habilitados: number;
desabilitados: number;
};
export type ApiResponse<T> = {
ok: boolean;
message?: string;
data: T;
};
export type MovimentosFixosListResponse = {
ok: boolean;
data: MovimentoFixo[];
pagination: MovimentosFixosPagination;
summary: MovimentosFixosSummary;
};

View File

@ -144,19 +144,33 @@ function origemColor(origem: string) {
return 'default'; return 'default';
} }
function criarItemManual(tipo: SimulacaoMensalTipoItem): SimulacaoMensalItem { function criarItemManual(
const id = `manual:${tipo}:${Date.now()}:${Math.random().toString(16).slice(2)}`; tipo: SimulacaoMensalTipoItem
): SimulacaoMensalItem {
const id =
`manual:${tipo}:${Date.now()}:${Math.random()
.toString(16)
.slice(2)}`;
return { return {
id, id,
tipo, tipo,
origem: 'manual', origem: 'manual',
descricao: tipo === 'entrada' ? 'Nova entrada' : 'Nova saída', descricao:
tipo === 'entrada'
? 'Nova entrada'
: 'Nova saída',
valor: 0, valor: 0,
valor_original: 0, valor_original: 0,
movimento: tipo === 'entrada' ? 'Entrada' : 'Saida', movimento:
tipo === 'entrada'
? 'Entrada'
: 'Saida',
status: 'Simulado', status: 'Simulado',
data: null, data: null,
investimento: 0,
editavel: true, editavel: true,
editado: true, editado: true,
}; };
@ -303,6 +317,30 @@ function SimulacaoLista({
} }
/> />
{!isEntrada && (
<TextField
select
label="Investimento"
value={Number(item.investimento || 0)}
size="small"
sx={{
width: {
xs: '100%',
md: 165,
},
}}
onChange={(event) =>
onAlterarItem(tipo, item.id, {
investimento: Number(event.target.value),
editado: true,
})
}
>
<MenuItem value={0}>Não</MenuItem>
<MenuItem value={1}>Sim</MenuItem>
</TextField>
)}
<Tooltip title="Remover da simulação"> <Tooltip title="Remover da simulação">
<IconButton <IconButton
color="error" color="error"
@ -396,6 +434,16 @@ function SimulacaoLista({
variant="outlined" variant="outlined"
/> />
)} )}
{!isEntrada && Number(item.investimento) === 1 && (
<Chip
size="small"
label="Investimento"
color="info"
icon={<AccountBalanceIcon />}
variant="outlined"
/>
)}
</Stack> </Stack>
</Stack> </Stack>
</Paper> </Paper>