Site/server/controllers/oriontard.pdf.controller.js

425 lines
9.8 KiB
JavaScript

'use strict';
require('../connector');
const express = require('express');
const router = express.Router();
const {
gerarOrdemServicoPdf,
gerarRelatorioOrdensServicoPdf
} = require('../services/orionPdfService');
function getPool() {
if (!global.ConexaoMySQL_Oriontard) {
throw new Error('Pool do banco Oriontard não foi inicializado.');
}
return global.ConexaoMySQL_Oriontard;
}
function dbQuery(sql, params = []) {
return new Promise((resolve, reject) => {
getPool().query(sql, params, (error, results, fields) => {
if (error) {
reject(error);
return;
}
resolve({ results, fields });
});
});
}
function positiveInteger(value, fieldName) {
const number = Number(value);
if (!Number.isInteger(number) || number <= 0) {
const error = new Error(`${fieldName} inválido.`);
error.httpStatus = 400;
throw error;
}
return number;
}
function sendError(req, res, error) {
const status = error.httpStatus || 500;
const message = status >= 500
? 'Erro interno ao gerar o relatório.'
: error.message;
console.error(
`${new Date().toUTCString()} - ${req.originalUrl} ${status} error`,
error
);
if (res.headersSent) {
res.destroy(error);
return;
}
res.status(status).json({
status,
error: {
code: error.code || null,
message
},
response: null
});
}
function dateToSql(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
function parseDateOrDefault(value, fallback) {
if (!value) {
return fallback;
}
const date = new Date(`${value}T00:00:00`);
if (Number.isNaN(date.getTime())) {
const error = new Error('Data de filtro inválida.');
error.httpStatus = 400;
throw error;
}
return date;
}
async function loadFullOrder(id) {
const [
{ results: orders },
{ results: activities },
{ results: technicians },
{ results: materials },
{ results: odometers },
{ results: payments }
] = await Promise.all([
dbQuery(
`
SELECT
os.*,
t.nome AS tecnico_responsavel_nome,
GREATEST(os.valor_total - os.valor_pago, 0) AS valor_pendente
FROM manut_ordens_servico os
LEFT JOIN manut_tecnicos t
ON t.id = os.tecnico_responsavel_id
WHERE os.id = ?
AND os.excluido_em IS NULL
LIMIT 1;
`,
[id]
),
dbQuery(
`
SELECT *
FROM manut_ordem_servico_atividades
WHERE ordem_servico_id = ?
ORDER BY data_atividade, ordem_exibicao, id;
`,
[id]
),
dbQuery(
`
SELECT
at.atividade_id,
at.tecnico_id,
at.papel,
at.horas_trabalhadas_expediente,
at.horas_trabalhadas_extras,
t.nome AS tecnico_nome
FROM manut_ordem_servico_atividade_tecnicos at
INNER JOIN manut_tecnicos t
ON t.id = at.tecnico_id
INNER JOIN manut_ordem_servico_atividades a
ON a.id = at.atividade_id
WHERE a.ordem_servico_id = ?
ORDER BY t.nome;
`,
[id]
),
dbQuery(
`
SELECT mat.*
FROM manut_ordem_servico_atividade_materiais mat
INNER JOIN manut_ordem_servico_atividades a
ON a.id = mat.atividade_id
WHERE a.ordem_servico_id = ?
ORDER BY mat.id;
`,
[id]
),
dbQuery(
`
SELECT *
FROM manut_equipamento_odometro_leituras
WHERE ordem_servico_id = ?
ORDER BY leitura_em, id;
`,
[id]
),
dbQuery(
`
SELECT *
FROM manut_ordem_servico_pagamentos
WHERE ordem_servico_id = ?
AND cancelado_em IS NULL
ORDER BY data_pagamento, id;
`,
[id]
)
]);
if (!orders.length) {
const error = new Error('Ordem de serviço não encontrada.');
error.httpStatus = 404;
throw error;
}
const techniciansByActivity = new Map();
technicians.forEach(item => {
if (!techniciansByActivity.has(item.atividade_id)) {
techniciansByActivity.set(item.atividade_id, []);
}
techniciansByActivity.get(item.atividade_id).push(item);
});
const materialsByActivity = new Map();
materials.forEach(item => {
if (!materialsByActivity.has(item.atividade_id)) {
materialsByActivity.set(item.atividade_id, []);
}
materialsByActivity.get(item.atividade_id).push(item);
});
const nestedActivities = activities.map(activity => ({
...activity,
tecnicos: techniciansByActivity.get(activity.id) || [],
materiais: materialsByActivity.get(activity.id) || []
}));
return {
...orders[0],
atividades: nestedActivities,
odometros_da_os: odometers,
pagamentos: payments
};
}
function buildReportFilters(query) {
const now = new Date();
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
const dataInicio = parseDateOrDefault(query.data_inicio, firstDay);
const dataFim = parseDateOrDefault(query.data_fim, lastDay);
if (dataFim < dataInicio) {
const error = new Error('A data final não pode ser anterior à data inicial.');
error.httpStatus = 400;
throw error;
}
const criterios = {
data_entrada: {
coluna: 'os.data_entrada',
label: 'data de entrada'
},
data_finalizacao: {
coluna: 'os.data_finalizacao',
label: 'data de finalização'
},
data_retirada: {
coluna: 'os.data_retirada',
label: 'data de retirada'
}
};
const criterio = criterios[query.criterio] || criterios.data_finalizacao;
return {
dataInicio,
dataFim,
dataInicioSql: dateToSql(dataInicio),
dataFimSql: dateToSql(dataFim),
criterio,
clienteId: query.cliente_id
? positiveInteger(query.cliente_id, 'Cliente')
: null,
equipamentoId: query.equipamento_id
? positiveInteger(query.equipamento_id, 'Equipamento')
: null,
status: query.status || null,
statusPagamento: query.status_pagamento || null,
tipo: query.tipo || null,
busca: query.busca ? String(query.busca).trim() : null,
cobrancaAberta:
query.cobranca_aberta === '1' ||
query.cobranca_aberta === 'true'
};
}
async function loadReportOrders(query) {
const filtros = buildReportFilters(query);
const where = [
'os.excluido_em IS NULL',
"os.status NOT IN ('RASCUNHO', 'CANCELADA')",
`${filtros.criterio.coluna} >= ?`,
`${filtros.criterio.coluna} < DATE_ADD(?, INTERVAL 1 DAY)`
];
const params = [
filtros.dataInicioSql,
filtros.dataFimSql
];
if (filtros.clienteId) {
where.push('os.cliente_id = ?');
params.push(filtros.clienteId);
}
if (filtros.equipamentoId) {
where.push('os.equipamento_id = ?');
params.push(filtros.equipamentoId);
}
if (filtros.status) {
where.push('os.status = ?');
params.push(filtros.status);
}
if (filtros.statusPagamento) {
where.push('os.status_pagamento = ?');
params.push(filtros.statusPagamento);
}
if (filtros.tipo) {
where.push('os.tipo = ?');
params.push(filtros.tipo);
}
if (filtros.cobrancaAberta) {
where.push("os.status_pagamento IN ('PENDENTE', 'PARCIAL')");
}
if (filtros.busca) {
const search = `%${filtros.busca}%`;
where.push(`(
CAST(os.id AS CHAR) LIKE ? OR
os.equipamento_nome_snapshot LIKE ? OR
os.numero_serie_snapshot LIKE ? OR
os.problema_relatado LIKE ? OR
os.cliente_nome_snapshot LIKE ?
)`);
params.push(search, search, search, search, search);
}
const { results } = await dbQuery(
`
SELECT
os.id,
os.cliente_id,
os.cliente_nome_snapshot,
os.equipamento_nome_snapshot,
os.numero_serie_snapshot,
os.tipo,
os.status,
os.status_pagamento,
os.data_entrada,
os.data_finalizacao,
os.data_retirada,
os.total_horas_expediente,
os.total_horas_extras,
os.valor_mao_obra,
os.valor_insumos,
os.valor_desconto,
os.valor_acrescimo,
os.valor_total,
os.valor_pago,
GREATEST(os.valor_total - os.valor_pago, 0) AS valor_pendente
FROM manut_ordens_servico os
WHERE ${where.join(' AND ')}
ORDER BY
os.cliente_nome_snapshot,
${filtros.criterio.coluna},
os.id;
`,
params
);
let clienteNome = null;
if (filtros.clienteId) {
const { results: clientes } = await dbQuery(
`
SELECT nome_fantasia
FROM manut_clientes
WHERE id = ?
LIMIT 1;
`,
[filtros.clienteId]
);
clienteNome = clientes.length
? clientes[0].nome_fantasia
: null;
}
return {
ordens: results,
filtros: {
dataInicio: filtros.dataInicio,
dataFim: filtros.dataFim,
criterioLabel: filtros.criterio.label,
clienteNome
}
};
}
var routes = function () {
router.get('/downloadOrdemServicoPdf/:id', async function (req, res) {
try {
const id = positiveInteger(req.params.id, 'Ordem de serviço');
const interno =
req.query.interno === '1' ||
req.query.interno === 'true';
const ordem = await loadFullOrder(id);
gerarOrdemServicoPdf(res, ordem, {
interno
});
} catch (error) {
sendError(req, res, error);
}
});
router.get('/downloadRelatorioOrdensServicoPdf', async function (req, res) {
try {
const resultado = await loadReportOrders(req.query || {});
gerarRelatorioOrdensServicoPdf(
res,
resultado.ordens,
resultado.filtros
);
} catch (error) {
sendError(req, res, error);
}
});
return router;
};
module.exports = routes;