Adicionado geração de movimentos fixos
This commit is contained in:
parent
0731328492
commit
66287cff29
|
|
@ -1,4 +1,5 @@
|
||||||
const movimentosService = require('../services/movimentos.service');
|
const movimentosService = require('../services/movimentos.service');
|
||||||
|
const movimentosFixosGeracaoService = require('../../movimentosFixos/services/movimentosFixosGeracao.service');
|
||||||
|
|
||||||
const MOVIMENTOS_VALIDOS = ['Entrada', 'Saida', 'Sangria', 'Estorno'];
|
const MOVIMENTOS_VALIDOS = ['Entrada', 'Saida', 'Sangria', 'Estorno'];
|
||||||
const STATUS_VALIDOS = ['Pago', 'A pagar', 'Recebido', 'A receber'];
|
const STATUS_VALIDOS = ['Pago', 'A pagar', 'Recebido', 'A receber'];
|
||||||
|
|
@ -120,6 +121,14 @@ function tratarErro(error, res, mensagemPadrao) {
|
||||||
|
|
||||||
async function listar(req, res) {
|
async function listar(req, res) {
|
||||||
try {
|
try {
|
||||||
|
const idUsuario = req.user.idusuarios || req.user.id;
|
||||||
|
|
||||||
|
const geracaoFixos = await movimentosFixosGeracaoService.gerarParaIntervalo({
|
||||||
|
dataInicio: req.query.dataInicio,
|
||||||
|
dataFim: req.query.dataFim,
|
||||||
|
idUsuario,
|
||||||
|
});
|
||||||
|
|
||||||
const resultado = await movimentosService.listarMovimentos({
|
const resultado = await movimentosService.listarMovimentos({
|
||||||
limite: req.query.limite,
|
limite: req.query.limite,
|
||||||
offset: req.query.offset,
|
offset: req.query.offset,
|
||||||
|
|
@ -144,7 +153,7 @@ async function listar(req, res) {
|
||||||
orderBy: req.query.orderBy,
|
orderBy: req.query.orderBy,
|
||||||
orderDirection: req.query.orderDirection,
|
orderDirection: req.query.orderDirection,
|
||||||
|
|
||||||
idusuarios: req.user.idusuarios || req.user.id,
|
idusuarios: idUsuario,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
|
|
@ -152,6 +161,7 @@ async function listar(req, res) {
|
||||||
data: resultado.data,
|
data: resultado.data,
|
||||||
pagination: resultado.pagination,
|
pagination: resultado.pagination,
|
||||||
summary: resultado.summary,
|
summary: resultado.summary,
|
||||||
|
movimentosFixosGerados: geracaoFixos,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao listar movimentos:', error);
|
console.error('Erro ao listar movimentos:', error);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,441 @@
|
||||||
|
const pool = require('../../../database/mysql');
|
||||||
|
|
||||||
|
function normalizarDataOuNull(valor) {
|
||||||
|
if (!valor) return null;
|
||||||
|
|
||||||
|
const texto = String(valor).slice(0, 10);
|
||||||
|
|
||||||
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(texto)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return texto;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hojeDataString() {
|
||||||
|
return new Date().toISOString().slice(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function competenciaAtual() {
|
||||||
|
return hojeDataString().slice(0, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
function primeiroDiaCompetencia(competencia) {
|
||||||
|
return `${competencia}-01`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ultimoDiaDoMes(ano, mes) {
|
||||||
|
return new Date(ano, mes, 0).getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function montarDataVencimento(competencia, diaVencimento) {
|
||||||
|
const [anoStr, mesStr] = competencia.split('-');
|
||||||
|
const ano = Number(anoStr);
|
||||||
|
const mes = Number(mesStr);
|
||||||
|
|
||||||
|
const ultimoDia = ultimoDiaDoMes(ano, mes);
|
||||||
|
const dia = Math.min(Math.max(Number(diaVencimento || 1), 1), ultimoDia);
|
||||||
|
|
||||||
|
return `${competencia}-${String(dia).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMesesCompetencia(competencia, meses) {
|
||||||
|
const [anoStr, mesStr] = competencia.split('-');
|
||||||
|
const data = new Date(Number(anoStr), Number(mesStr) - 1, 1);
|
||||||
|
|
||||||
|
data.setMonth(data.getMonth() + meses);
|
||||||
|
|
||||||
|
const ano = data.getFullYear();
|
||||||
|
const mes = String(data.getMonth() + 1).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${ano}-${mes}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function listarCompetenciasEntre(dataInicio, dataFim) {
|
||||||
|
const inicio = normalizarDataOuNull(dataInicio) || hojeDataString();
|
||||||
|
const fim = normalizarDataOuNull(dataFim) || inicio;
|
||||||
|
|
||||||
|
let competenciaInicio = inicio.slice(0, 7);
|
||||||
|
const competenciaFim = fim.slice(0, 7);
|
||||||
|
const atual = competenciaAtual();
|
||||||
|
|
||||||
|
// Regra anti-retroativo:
|
||||||
|
// se o filtro começa no passado, começa no mês atual.
|
||||||
|
if (competenciaInicio < atual) {
|
||||||
|
competenciaInicio = atual;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (competenciaInicio > competenciaFim) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const competencias = [];
|
||||||
|
let cursor = competenciaInicio;
|
||||||
|
|
||||||
|
while (cursor <= competenciaFim) {
|
||||||
|
competencias.push(cursor);
|
||||||
|
cursor = addMesesCompetencia(cursor, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return competencias;
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusInicialPorMovimento(movimento) {
|
||||||
|
if (movimento === 'Entrada' || movimento === 'Estorno') {
|
||||||
|
return 'A receber';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (movimento === 'Saida') {
|
||||||
|
return 'A pagar';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (movimento === 'Sangria') {
|
||||||
|
return 'Pago';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'A pagar';
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataBaixaInicialPorMovimento(movimento, dataVencimento) {
|
||||||
|
if (movimento === 'Sangria') {
|
||||||
|
return dataVencimento;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buscarFixosDoUsuario(connection, idUsuario) {
|
||||||
|
const [rows] = await connection.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
mf.idmovimentosfixos,
|
||||||
|
mf.movimento,
|
||||||
|
mf.descricao,
|
||||||
|
mf.valor,
|
||||||
|
mf.dia_vencimento,
|
||||||
|
mf.idcentrodecustos,
|
||||||
|
mf.idbancos
|
||||||
|
FROM movimentosfixos mf
|
||||||
|
INNER JOIN bancos b ON b.idbancos = mf.idbancos
|
||||||
|
WHERE mf.habilitado = 1
|
||||||
|
AND mf.gerar_automatico = 1
|
||||||
|
AND b.idusuarios = ?
|
||||||
|
ORDER BY mf.idmovimentosfixos ASC
|
||||||
|
`,
|
||||||
|
[idUsuario]
|
||||||
|
);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jaExisteMovimentoFixo(connection, fixo, competencia) {
|
||||||
|
const [rowsGeracao] = await connection.query(
|
||||||
|
`
|
||||||
|
SELECT idmovimentosfixosgeracoes, idcontasapagar, status
|
||||||
|
FROM movimentosfixosgeracoes
|
||||||
|
WHERE idmovimentosfixos = ?
|
||||||
|
AND competencia = ?
|
||||||
|
LIMIT 1
|
||||||
|
FOR UPDATE
|
||||||
|
`,
|
||||||
|
[fixo.idmovimentosfixos, competencia]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rowsGeracao.length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [rowsMovimentoVinculado] = await connection.query(
|
||||||
|
`
|
||||||
|
SELECT idcontasapagar
|
||||||
|
FROM contasapagar
|
||||||
|
WHERE idmovimentosfixos = ?
|
||||||
|
AND competencia = ?
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
LIMIT 1
|
||||||
|
FOR UPDATE
|
||||||
|
`,
|
||||||
|
[fixo.idmovimentosfixos, competencia]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rowsMovimentoVinculado.length > 0) {
|
||||||
|
await connection.query(
|
||||||
|
`
|
||||||
|
INSERT INTO movimentosfixosgeracoes (
|
||||||
|
idmovimentosfixos,
|
||||||
|
competencia,
|
||||||
|
idcontasapagar,
|
||||||
|
status,
|
||||||
|
mensagem,
|
||||||
|
insert_date,
|
||||||
|
update_date
|
||||||
|
) VALUES (?, ?, ?, 'existente', 'Movimento fixo já existia vinculado em contasapagar.', NOW(), NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
idcontasapagar = VALUES(idcontasapagar),
|
||||||
|
status = VALUES(status),
|
||||||
|
mensagem = VALUES(mensagem),
|
||||||
|
update_date = NOW()
|
||||||
|
`,
|
||||||
|
[fixo.idmovimentosfixos, competencia, rowsMovimentoVinculado[0].idcontasapagar]
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [rowsMovimentoLegado] = await connection.query(
|
||||||
|
`
|
||||||
|
SELECT idcontasapagar
|
||||||
|
FROM contasapagar
|
||||||
|
WHERE deleted_at IS NULL
|
||||||
|
AND idmovimentosfixos IS NULL
|
||||||
|
AND competencia IS NULL
|
||||||
|
AND movimento = ?
|
||||||
|
AND descricao = ?
|
||||||
|
AND idbancos = ?
|
||||||
|
AND (
|
||||||
|
(? IS NULL AND idcentrodecustos IS NULL)
|
||||||
|
OR idcentrodecustos = ?
|
||||||
|
)
|
||||||
|
AND DATE(datavencimento) BETWEEN ? AND ?
|
||||||
|
ORDER BY idcontasapagar DESC
|
||||||
|
LIMIT 1
|
||||||
|
FOR UPDATE
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
fixo.movimento,
|
||||||
|
fixo.descricao,
|
||||||
|
fixo.idbancos,
|
||||||
|
fixo.idcentrodecustos || null,
|
||||||
|
fixo.idcentrodecustos || null,
|
||||||
|
`${competencia}-01`,
|
||||||
|
`${competencia}-31`,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rowsMovimentoLegado.length > 0) {
|
||||||
|
const idContasPagar = rowsMovimentoLegado[0].idcontasapagar;
|
||||||
|
|
||||||
|
await connection.query(
|
||||||
|
`
|
||||||
|
UPDATE contasapagar
|
||||||
|
SET
|
||||||
|
idmovimentosfixos = ?,
|
||||||
|
competencia = ?,
|
||||||
|
origem = COALESCE(origem, 'movimento_fixo_legado'),
|
||||||
|
update_date = NOW()
|
||||||
|
WHERE idcontasapagar = ?
|
||||||
|
`,
|
||||||
|
[fixo.idmovimentosfixos, competencia, idContasPagar]
|
||||||
|
);
|
||||||
|
|
||||||
|
await connection.query(
|
||||||
|
`
|
||||||
|
INSERT INTO movimentosfixosgeracoes (
|
||||||
|
idmovimentosfixos,
|
||||||
|
competencia,
|
||||||
|
idcontasapagar,
|
||||||
|
status,
|
||||||
|
mensagem,
|
||||||
|
insert_date,
|
||||||
|
update_date
|
||||||
|
) VALUES (?, ?, ?, 'existente', 'Movimento legado vinculado automaticamente ao fixo.', NOW(), NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
idcontasapagar = VALUES(idcontasapagar),
|
||||||
|
status = VALUES(status),
|
||||||
|
mensagem = VALUES(mensagem),
|
||||||
|
update_date = NOW()
|
||||||
|
`,
|
||||||
|
[fixo.idmovimentosfixos, competencia, idContasPagar]
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function inserirMovimentoFixoGerado(connection, fixo, competencia, idUsuario) {
|
||||||
|
const dataEntrada = primeiroDiaCompetencia(competencia);
|
||||||
|
const dataVencimento = montarDataVencimento(competencia, fixo.dia_vencimento);
|
||||||
|
const status = statusInicialPorMovimento(fixo.movimento);
|
||||||
|
const dataBaixa = dataBaixaInicialPorMovimento(fixo.movimento, dataVencimento);
|
||||||
|
|
||||||
|
const [result] = await connection.query(
|
||||||
|
`
|
||||||
|
INSERT INTO contasapagar (
|
||||||
|
movimento,
|
||||||
|
descricao,
|
||||||
|
dataentrada,
|
||||||
|
datavencimento,
|
||||||
|
databaixa,
|
||||||
|
valor,
|
||||||
|
parcela,
|
||||||
|
parcelas,
|
||||||
|
status,
|
||||||
|
idcentrodecustos,
|
||||||
|
idbancos,
|
||||||
|
idusuarios_cad,
|
||||||
|
idusuarios_baixa,
|
||||||
|
idclientes,
|
||||||
|
idveiculosdetalhes,
|
||||||
|
idbancos_p,
|
||||||
|
idmovimentosfixos,
|
||||||
|
competencia,
|
||||||
|
grupo_parcelamento,
|
||||||
|
origem,
|
||||||
|
saldo_processado,
|
||||||
|
observacao,
|
||||||
|
insert_date,
|
||||||
|
update_date,
|
||||||
|
deleted_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, 1, 1, ?, ?, ?, ?, ?, NULL, NULL, NULL, ?, ?, NULL, 'movimento_fixo', 0, NULL, NOW(), NOW(), NULL)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
fixo.movimento,
|
||||||
|
fixo.descricao,
|
||||||
|
dataEntrada,
|
||||||
|
dataVencimento,
|
||||||
|
dataBaixa,
|
||||||
|
Number(fixo.valor || 0),
|
||||||
|
status,
|
||||||
|
fixo.idcentrodecustos || null,
|
||||||
|
fixo.idbancos || null,
|
||||||
|
idUsuario,
|
||||||
|
dataBaixa ? idUsuario : null,
|
||||||
|
fixo.idmovimentosfixos,
|
||||||
|
competencia,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const idContasPagar = result.insertId;
|
||||||
|
|
||||||
|
await connection.query(
|
||||||
|
`
|
||||||
|
INSERT INTO movimentosfixos_geracoes (
|
||||||
|
idmovimentosfixos,
|
||||||
|
competencia,
|
||||||
|
idcontasapagar,
|
||||||
|
status,
|
||||||
|
mensagem,
|
||||||
|
insert_date,
|
||||||
|
update_date
|
||||||
|
) VALUES (?, ?, ?, 'gerado', 'Movimento gerado automaticamente.', NOW(), NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
idcontasapagar = VALUES(idcontasapagar),
|
||||||
|
status = VALUES(status),
|
||||||
|
mensagem = VALUES(mensagem),
|
||||||
|
update_date = NOW()
|
||||||
|
`,
|
||||||
|
[fixo.idmovimentosfixos, competencia, idContasPagar]
|
||||||
|
);
|
||||||
|
|
||||||
|
await connection.query(
|
||||||
|
`
|
||||||
|
UPDATE movimentosfixos
|
||||||
|
SET
|
||||||
|
ultimo_mes_gerado = CASE
|
||||||
|
WHEN ultimo_mes_gerado IS NULL OR ultimo_mes_gerado < ? THEN ?
|
||||||
|
ELSE ultimo_mes_gerado
|
||||||
|
END,
|
||||||
|
update_date = NOW()
|
||||||
|
WHERE idmovimentosfixos = ?
|
||||||
|
`,
|
||||||
|
[competencia, competencia, fixo.idmovimentosfixos]
|
||||||
|
);
|
||||||
|
|
||||||
|
return idContasPagar;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function gerarParaIntervalo(opcoes = {}) {
|
||||||
|
const idUsuario = Number(opcoes.idUsuario || opcoes.idusuarios || 0);
|
||||||
|
|
||||||
|
if (!idUsuario) {
|
||||||
|
return {
|
||||||
|
gerados: 0,
|
||||||
|
ignorados: 0,
|
||||||
|
competencias: [],
|
||||||
|
detalhes: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const competencias = listarCompetenciasEntre(opcoes.dataInicio, opcoes.dataFim);
|
||||||
|
|
||||||
|
if (competencias.length === 0) {
|
||||||
|
return {
|
||||||
|
gerados: 0,
|
||||||
|
ignorados: 0,
|
||||||
|
competencias: [],
|
||||||
|
detalhes: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const connection = await pool.getConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connection.beginTransaction();
|
||||||
|
|
||||||
|
const fixos = await buscarFixosDoUsuario(connection, idUsuario);
|
||||||
|
|
||||||
|
let gerados = 0;
|
||||||
|
let ignorados = 0;
|
||||||
|
const detalhes = [];
|
||||||
|
|
||||||
|
for (const competencia of competencias) {
|
||||||
|
for (const fixo of fixos) {
|
||||||
|
const existe = await jaExisteMovimentoFixo(
|
||||||
|
connection,
|
||||||
|
fixo,
|
||||||
|
competencia
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existe) {
|
||||||
|
ignorados++;
|
||||||
|
|
||||||
|
detalhes.push({
|
||||||
|
idmovimentosfixos: fixo.idmovimentosfixos,
|
||||||
|
competencia,
|
||||||
|
status: 'ignorado',
|
||||||
|
mensagem: 'Já existia geração ou movimento vinculado.',
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idcontasapagar = await inserirMovimentoFixoGerado(
|
||||||
|
connection,
|
||||||
|
fixo,
|
||||||
|
competencia,
|
||||||
|
idUsuario
|
||||||
|
);
|
||||||
|
|
||||||
|
gerados++;
|
||||||
|
|
||||||
|
detalhes.push({
|
||||||
|
idmovimentosfixos: fixo.idmovimentosfixos,
|
||||||
|
competencia,
|
||||||
|
idcontasapagar,
|
||||||
|
status: 'gerado',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await connection.commit();
|
||||||
|
|
||||||
|
return {
|
||||||
|
gerados,
|
||||||
|
ignorados,
|
||||||
|
competencias,
|
||||||
|
detalhes,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
await connection.rollback();
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
connection.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
gerarParaIntervalo,
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue