Commit inicial - Sistema de Gestion Hotelera Hacienda San Angel
- Backend Node.js/Express con PostgreSQL - Frontend React 19 con Vite - Docker Compose para orquestacion - Documentacion completa en README.md - Scripts SQL para base de datos - Configuracion de ejemplo (.env.example)
This commit is contained in:
51
backend/hotel_hacienda/src/app.js
Normal file
51
backend/hotel_hacienda/src/app.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const cors = require('cors');
|
||||
require("dotenv").config();
|
||||
|
||||
const corsOptions = {
|
||||
origin: process.env.URL_CORS, // sin slash al final
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allowedHeaders: ["Content-Type", "Authorization"]
|
||||
};
|
||||
app.use(cors(corsOptions));
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
//rutas
|
||||
const authRoutes = require('./routes/auth.routes');
|
||||
const employeeRoutes = require('./routes/employee.routes');
|
||||
const contractsRoutes = require('./routes/contract.routes');
|
||||
const reportcontractRoutes = require('./routes/reportcontract.routes');
|
||||
const productRoutes = require('./routes/product.routes');
|
||||
const expenseRoutes = require('./routes/expense.routes');
|
||||
const statusRoutes = require('./routes/status.routes');
|
||||
const paymentRoutes = require('./routes/payment.routes');
|
||||
const settingsRoutes = require('./routes/settings.routes');
|
||||
const emailRoutes = require('./routes/mail.routes');
|
||||
const incomeRoutes = require('./routes/incomes.routes');
|
||||
const purchaseRoutes = require('./routes/purchase.routes');
|
||||
const exchangeRoutes = require('./routes/exchange.routes');
|
||||
const hotelRoutes = require('./routes/hotelpl.routes');
|
||||
const restaurantRoutes = require('./routes/restaurantpl.routes');
|
||||
const incomehrxRoutes = require('./routes/incomehrx.routes');
|
||||
|
||||
//Prefijo
|
||||
app.use('/api/employees', employeeRoutes);
|
||||
app.use('/api/contracts', contractsRoutes);
|
||||
app.use('/api/reportcontracts', reportcontractRoutes);
|
||||
app.use('/api/products', productRoutes);
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/expenses', expenseRoutes);
|
||||
app.use('/api/status', statusRoutes);
|
||||
app.use('/api/payment', paymentRoutes);
|
||||
app.use('/api/settings',settingsRoutes);
|
||||
app.use('/api/emails',emailRoutes);
|
||||
app.use('/api/incomes',incomeRoutes);
|
||||
app.use('/api/incomeshrx',incomehrxRoutes);
|
||||
app.use('/api/purchases',purchaseRoutes);
|
||||
app.use('/api/exchange',exchangeRoutes);
|
||||
app.use('/api/hotelpl',hotelRoutes);
|
||||
app.use('/api/restaurantpl',restaurantRoutes);
|
||||
|
||||
module.exports = app;
|
||||
102
backend/hotel_hacienda/src/controllers/auth.controller.js
Normal file
102
backend/hotel_hacienda/src/controllers/auth.controller.js
Normal file
@@ -0,0 +1,102 @@
|
||||
const pool = require('../db/connection');
|
||||
const transporter = require('../services/mailService');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const login = async (req, res) => {
|
||||
const { name_mail_user, user_pass } = req.body;
|
||||
|
||||
try {
|
||||
const result = await pool.query('SELECT * from validarusuario($1, $2)', [name_mail_user, user_pass]);
|
||||
const { status, rol, user_id,user_name } = result.rows[0];
|
||||
|
||||
|
||||
let message = '';
|
||||
switch (status) {
|
||||
case 1:
|
||||
message = 'Usuario autenticado correctamente';
|
||||
break;
|
||||
case 2:
|
||||
message = 'Usuario o contraseña incorrectos';
|
||||
break;
|
||||
default:
|
||||
message = 'Error desconocido en la validación';
|
||||
}
|
||||
|
||||
res.json({
|
||||
rol,
|
||||
user_id,
|
||||
user_name,
|
||||
message });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'Error interno del servidor' });
|
||||
}
|
||||
};
|
||||
const createuser = async (req, res) => {
|
||||
const { name_user,id_rol, email,user_pass } = req.body;
|
||||
|
||||
try {
|
||||
const result = await pool.query('SELECT createuser($1, $2, $3, $4) AS status', [name_user, id_rol, email, user_pass]);
|
||||
|
||||
const status = result.rows[0].status;
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se agrego el usuario correctamente",
|
||||
status});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo agregar al usuario' });
|
||||
}
|
||||
};
|
||||
|
||||
const passRecover = async (req, res) => {
|
||||
try {
|
||||
const {user_mail } = req.body;
|
||||
|
||||
const new_pass = crypto
|
||||
.randomBytes(12)
|
||||
.toString('base64')
|
||||
.slice(0, 12);
|
||||
|
||||
const result = await pool.query(
|
||||
'SELECT reppassuser($1,$2) AS status',
|
||||
[user_mail, new_pass]
|
||||
);
|
||||
|
||||
const status = result.rows[0].status;
|
||||
|
||||
if (status !== 1) {
|
||||
return res.json({
|
||||
message: 'Correo NO registrado.'
|
||||
});
|
||||
}
|
||||
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: user_mail,
|
||||
subject: `Nueva contraseña`,
|
||||
html: `
|
||||
<h2>Recuperación de contraseña</h2>
|
||||
<p>Se generó una nueva contraseña para el correo <b>${user_mail}</b></p>
|
||||
<p><b>Contraseña:</b> ${new_pass}</p>
|
||||
<p>Por favor mantenla y borra el correo una vez resguardada.</p>
|
||||
`,
|
||||
};
|
||||
|
||||
await transporter.sendMail(mailOptions);
|
||||
|
||||
res.json({
|
||||
message: 'Correo enviado con nueva contraseña.',
|
||||
status
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({
|
||||
message: 'No se pudo mandar el correo de recuperación'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { login,createuser,passRecover};
|
||||
298
backend/hotel_hacienda/src/controllers/contract.controller.js
Normal file
298
backend/hotel_hacienda/src/controllers/contract.controller.js
Normal file
@@ -0,0 +1,298 @@
|
||||
const pool = require('../db/connection');
|
||||
const transporter = require('../services/mailService'); // importa el transporter
|
||||
|
||||
const getContracts = async (req, res) => {
|
||||
try {
|
||||
// Leer query params con valores por defecto
|
||||
const page = parseInt(req.query.page) || 1; // Página actual
|
||||
const limit = parseInt(req.query.limit) || 1000; // Cantidad por página
|
||||
const offset = (page - 1) * limit; // Desde dónde empezar
|
||||
|
||||
// Llamamos a la función con LIMIT y OFFSET
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM getcontracts() LIMIT $1 OFFSET $2`,
|
||||
[limit, offset]
|
||||
);
|
||||
|
||||
// Obtener total para calcular páginas
|
||||
const totalResult = await pool.query('SELECT COUNT(*) FROM contracts');
|
||||
const total = parseInt(totalResult.rows[0].count);
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
const contracts = result.rows.map(con => ({
|
||||
id_contract: con.id_contract,
|
||||
name_employee: con.name_employee,
|
||||
position_employee: con.position_employee,
|
||||
area_employee: con.area_employee,
|
||||
contract_start: con.contratc_start,
|
||||
contract_end: con.contratc_end,
|
||||
uniforms: con.uniforms,
|
||||
daily_pay: con.daily_pay,
|
||||
status_contract: con.status_contract,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages,
|
||||
data: contracts
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los contratos' });
|
||||
}
|
||||
};
|
||||
//CONTRATOS CERCA DE TERMINAR, 1 MES O MENOS DE TIEMPO
|
||||
const neartoend = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM contratcsneartoend() as contractsneartoend',
|
||||
);
|
||||
const neartoend = result.rows[0].contractsneartoend;
|
||||
|
||||
res.json({
|
||||
message: 'Contratos cerca de terminar',
|
||||
data: neartoend
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los contratos' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//listado de posiciones
|
||||
const getpositions = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM getpositions()',
|
||||
);
|
||||
const positions = result.rows.map(pos => ({
|
||||
id_position: pos.id_position,
|
||||
name_position: pos.name_position,
|
||||
spanish_name: pos.spanish_name
|
||||
}));
|
||||
|
||||
res.json({
|
||||
data:positions
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener las posiciones' });
|
||||
}
|
||||
};
|
||||
|
||||
const getareas = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM getareas()',
|
||||
);
|
||||
const areas = result.rows.map(ar => ({
|
||||
id_area: ar.id_area,
|
||||
name_area: ar.name_area,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
data:areas
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener las areas' });
|
||||
}
|
||||
};
|
||||
|
||||
const newContract = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
rfc_emp,
|
||||
id_position_emp,
|
||||
id_area_emp,
|
||||
start_contract,
|
||||
end_contract,
|
||||
new_daily_pay,
|
||||
boss_rfc,
|
||||
boss_name,
|
||||
uniforms} = req.body;
|
||||
let message = "";
|
||||
|
||||
const result = await pool.query('SELECT newcontract($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb) AS status',
|
||||
[rfc_emp,id_position_emp,id_area_emp,start_contract,end_contract,new_daily_pay,boss_rfc,boss_name,JSON.stringify(uniforms)]);
|
||||
const newcontract = result.rows[0].status;
|
||||
if(newcontract == 1)
|
||||
message = "Contrato generado correctamente";
|
||||
else
|
||||
message = "No se pudo agregar el contrato"
|
||||
res.json({
|
||||
message,
|
||||
idNewContract: newcontract
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo generar el nuevo contrato' });
|
||||
}
|
||||
}
|
||||
const updateContract = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
rfc_emp,
|
||||
id_position_emp,
|
||||
id_area_emp,
|
||||
start_contract,
|
||||
end_contract,
|
||||
status_contract,
|
||||
new_daily_pay,
|
||||
boss_rfc,
|
||||
boss_name,
|
||||
reason,
|
||||
uniforms} = req.body;
|
||||
const { id } = req.params;
|
||||
let message = "";
|
||||
|
||||
const result = await pool.query('SELECT updatecontract($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb) AS status',
|
||||
[id,rfc_emp,id_position_emp,id_area_emp,start_contract,end_contract,status_contract,new_daily_pay,boss_rfc,boss_name,reason,JSON.stringify(uniforms)]);
|
||||
const newcontract = result.rows[0].status;
|
||||
if(newcontract == 1)
|
||||
message = "Contrato actualizado correctamente";
|
||||
else
|
||||
message = "No se pudo actualizar el contrato"
|
||||
res.json({
|
||||
message,
|
||||
idNewContract: newcontract
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo generar el nuevo contrato' });
|
||||
}
|
||||
};
|
||||
|
||||
const getContract = async (req, res) => {
|
||||
try {
|
||||
|
||||
const { id } = req.params;
|
||||
console.log(id);
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM getcontracts() WHERE id_contract = $1 LIMIT 1 `,
|
||||
[id]
|
||||
);
|
||||
const contracts = result.rows.map(con => ({
|
||||
id_contract: con.id_contract,
|
||||
name_employee: con.name_employee,
|
||||
position_employee: con.position_employee,
|
||||
area_employee: con.area_employee,
|
||||
contract_start: con.contratc_start,
|
||||
contract_end: con.contratc_end,
|
||||
uniforms: con.uniforms,
|
||||
daily_pay: con.daily_pay,
|
||||
status_contract: con.status_contract
|
||||
}));
|
||||
|
||||
res.json({
|
||||
|
||||
data: contracts
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los contratos' , error});
|
||||
}
|
||||
};
|
||||
|
||||
const getbosses = async (req, res) => {
|
||||
try {
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM getbosses()`
|
||||
);
|
||||
const bosses = result.rows.map(con => ({
|
||||
rfc_boss: con.rfc_boss,
|
||||
name_boss: con.name_boss,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
|
||||
data: bosses
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudieron obtener los jefes' , error});
|
||||
}
|
||||
};
|
||||
|
||||
const disabledcontracts = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM disabledcontracts() as total',
|
||||
);
|
||||
const disabled = result.rows[0].contractsneartoend;
|
||||
|
||||
res.json({
|
||||
message: "contratos expirados",
|
||||
data:disabled
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener las areas' });
|
||||
}
|
||||
};
|
||||
const reportEmployeeContract = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM reportemployeecontract()',
|
||||
);
|
||||
const report = result.rows.map(rep => ({
|
||||
name_employee: rep.name_employee,
|
||||
rfc_employee: rep.rfc_employee,
|
||||
nss_employee: rep.nss_employee,
|
||||
name_position: rep.name_position,
|
||||
spanish_name: rep.spanish_name,
|
||||
name_area: rep.name_area,
|
||||
addres_employee: rep.name_area,
|
||||
phone_employee: rep.phone_employee,
|
||||
email_employee: rep.email_employee ,
|
||||
birthday: rep.birthday,
|
||||
curp: rep.curp ,
|
||||
name_study: rep.name_study,
|
||||
id_contract: rep.id_contract ,
|
||||
contratc_start:rep.contract_start,
|
||||
contratc_end: rep.contract_end,
|
||||
name_status: rep.name_status ,
|
||||
daily_pay:rep.daily_pay ,
|
||||
rfc_boss: rep.rfc_boss ,
|
||||
name_boss: rep.name_boss ,
|
||||
reasonfordismissal: rep.reasonfordismissal ,
|
||||
name_contact: rep.name_contact,
|
||||
tel_contact: rep.tel_contact ,
|
||||
name_relationship: rep.name_relationship
|
||||
|
||||
}));
|
||||
|
||||
res.json({
|
||||
data:report
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el reporte' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getContract,
|
||||
getContracts,
|
||||
neartoend,
|
||||
getpositions,
|
||||
getareas,
|
||||
newContract,
|
||||
updateContract,
|
||||
disabledcontracts,
|
||||
getbosses,
|
||||
reportEmployeeContract
|
||||
};
|
||||
269
backend/hotel_hacienda/src/controllers/employee.controller.js
Normal file
269
backend/hotel_hacienda/src/controllers/employee.controller.js
Normal file
@@ -0,0 +1,269 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const getEmployees = async (req, res) => {
|
||||
try {
|
||||
// Leer query params con valores por defecto
|
||||
const page = parseInt(req.query.page) || 1; // Página actual
|
||||
const limit = parseInt(req.query.limit) || 500; // Cantidad por página
|
||||
const offset = (page - 1) * limit; // Desde dónde empezar
|
||||
|
||||
// Llamamos a la función con LIMIT y OFFSET
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM getemployees() LIMIT $1 OFFSET $2`,
|
||||
[limit, offset]
|
||||
);
|
||||
|
||||
// Obtener total para calcular páginas
|
||||
const totalResult = await pool.query('SELECT COUNT(*) FROM employees');
|
||||
const total = parseInt(totalResult.rows[0].count);
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
const employees = result.rows.map(emp => ({
|
||||
employee_rfc: emp.employee_rfc,
|
||||
name_employee: emp.name_employee,
|
||||
nss_employe: emp.nss_employe,
|
||||
position_employee: emp.position_employee,
|
||||
area_employee: emp.area_employee,
|
||||
phone_employee: emp.phone_employee,
|
||||
end_contract: emp.end_contract,
|
||||
daily_pay: emp.daily_pay,
|
||||
uniforms: emp.uniforms,
|
||||
status: emp.status,
|
||||
birthday: emp.birthday,
|
||||
curp: emp.curp
|
||||
}));
|
||||
|
||||
res.json({
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages,
|
||||
data: employees
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener empleados' });
|
||||
}
|
||||
};
|
||||
|
||||
//TOTAL DE EMPLEADOS CON CONTRATO ACTIVO
|
||||
const getTotalActiveEmployees = async (req, res) => {
|
||||
try {
|
||||
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM activeemployeesnumber() as activenumber',
|
||||
);
|
||||
const activeEmployees = result.rows[0].activenumber;
|
||||
|
||||
res.json({
|
||||
message: 'Total de empleados activos',
|
||||
data: activeEmployees
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el total de empleados ' });
|
||||
}
|
||||
};
|
||||
|
||||
//OBTENER UN SOLO EMPLEADO
|
||||
const getEmployee = async (req, res) => {
|
||||
try {
|
||||
const { rfcEmployee} = req.body;
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM getoneemployee($1)',
|
||||
[rfcEmployee]
|
||||
);
|
||||
|
||||
const employee = result.rows.map(emp => ({
|
||||
name_emp: emp.name_emp,
|
||||
rfc_emp: emp.rfc_emp,
|
||||
nss_emp: emp.nss_emp,
|
||||
id_position_emp: emp.id_position_emp,
|
||||
id_area_emp: emp.id_area_emp,
|
||||
addres_emp:emp.addres_emp,
|
||||
phone_emp: emp.phone_emp,
|
||||
email_emp: emp.email_emp,
|
||||
daily_pay: emp.daily_pay,
|
||||
uniforms: emp.uniforms,
|
||||
birthday: emp.birthday,
|
||||
curp: emp.curp
|
||||
}));
|
||||
|
||||
if (result.rows.length > 0) {
|
||||
res.json({
|
||||
message: 'Empleado existio previamente',
|
||||
data: employee
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
res.json({
|
||||
message: 'El pleneado no existe.',
|
||||
data: 0 // Devuelve la primera fila (o todas si quieres)
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener empleado' });
|
||||
}
|
||||
}
|
||||
|
||||
const newEmployee = async(req,res)=>{
|
||||
try{
|
||||
const {
|
||||
name_emp,
|
||||
rfc_emp,
|
||||
nss_emp,
|
||||
addres_emp,
|
||||
phone_emp,
|
||||
email_emp,
|
||||
birthday_emp,
|
||||
curp_emp,
|
||||
study_emp ,
|
||||
emergency_name ,
|
||||
emergency_tel,
|
||||
relationship_id
|
||||
} = req.body;
|
||||
const result = await pool.query('SELECT newemployee($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12) AS STATUS',
|
||||
[name_emp,rfc_emp,nss_emp,addres_emp,phone_emp,email_emp,birthday_emp,curp_emp, study_emp, emergency_name, emergency_tel,relationship_id]
|
||||
);
|
||||
const status = result.rows[0].status;
|
||||
if(status == 1)
|
||||
message = "Empleado añadido correctamente";
|
||||
else
|
||||
message = "Empleado no se pudo agregar"
|
||||
res.json({
|
||||
message,
|
||||
status: status
|
||||
});
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ error });
|
||||
}
|
||||
|
||||
}
|
||||
const updateEmployee = async(req,res)=>{
|
||||
try{
|
||||
const {
|
||||
name_emp,
|
||||
rfc_emp,
|
||||
nss_emp,
|
||||
addres_emp,
|
||||
phone_emp,
|
||||
email_emp,
|
||||
birthday_emp,
|
||||
curp_emp,
|
||||
study_emp ,
|
||||
emergency_name ,
|
||||
emergency_tel,
|
||||
relationship_id
|
||||
} = req.body;
|
||||
const result = await pool.query('SELECT updateemployee($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12) AS STATUS',
|
||||
[name_emp,rfc_emp,nss_emp,addres_emp,phone_emp,email_emp,birthday_emp,curp_emp, study_emp,
|
||||
emergency_name, emergency_tel, relationship_id]
|
||||
);
|
||||
const status = result.rows[0].status;
|
||||
if(status == 1)
|
||||
message = "Empleado actualizado correctamente";
|
||||
else
|
||||
message = "No se pudo agregar el empleado"
|
||||
res.json({
|
||||
message,
|
||||
status: status
|
||||
});
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ error });
|
||||
}
|
||||
|
||||
}
|
||||
const getattendance = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM getattendance()',
|
||||
);
|
||||
|
||||
const attendance = result.rows.map(emp => ({
|
||||
aid_attendance: emp.aid_attendance,
|
||||
aname_emp: emp.aname_emp,
|
||||
alast_name: emp.alast_name,
|
||||
aID_tran: emp.aID_tran,
|
||||
arol: emp.arol,
|
||||
adate_atten:emp.adate_atten,
|
||||
atime_atten: emp.atime_atten,
|
||||
alaboral_day: emp.alaboral_day,
|
||||
adata_origin: emp.adata_origin,
|
||||
aname_device: emp.aname_device,
|
||||
aserie_device: emp.aserie_device,
|
||||
aperforation: emp.aperforation,
|
||||
aubication: emp.aubication,
|
||||
aobservations: emp.aobservations
|
||||
}));
|
||||
|
||||
res.json({
|
||||
message: 'attendance',
|
||||
data: attendance
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener checador' });
|
||||
}
|
||||
};
|
||||
|
||||
const getGradeOfStudy = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM degreeofstudy',
|
||||
);
|
||||
|
||||
const study = result.rows.map(emp => ({
|
||||
id_study: emp.id_study,
|
||||
name_study: emp.name_study
|
||||
}));
|
||||
|
||||
res.json({
|
||||
message: 'grados de estudio',
|
||||
data: study
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los grados de estudio' });
|
||||
}
|
||||
};
|
||||
const getRelationshipEmployee = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM relationship_employee',
|
||||
);
|
||||
|
||||
const relationship = result.rows.map(emp => ({
|
||||
id_relationship: emp.id_relationship,
|
||||
name_relationship: emp.name_relationship
|
||||
}));
|
||||
|
||||
res.json({
|
||||
message: 'Parentesco',
|
||||
data: relationship
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los parentescos' });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports =
|
||||
{ getEmployees,
|
||||
getTotalActiveEmployees,
|
||||
getEmployee,
|
||||
newEmployee,
|
||||
updateEmployee,
|
||||
getattendance,
|
||||
getGradeOfStudy,
|
||||
getRelationshipEmployee
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
const pool = require('../db/connection');
|
||||
const axios = require('axios');
|
||||
const BANXICO_SERIE = 'SF43718';
|
||||
|
||||
function formatDate(date) {
|
||||
const d = new Date(date);
|
||||
const year = d.getFullYear();
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function mapBanxicoData(bmxJson) {
|
||||
const serie = bmxJson.bmx.series[0];
|
||||
return serie.datos.map(item => {
|
||||
const [day, month, year] = item.fecha.split('/');
|
||||
return {
|
||||
fecha: `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`,
|
||||
valor: parseFloat(item.dato)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const consultExchange = async (req, res) => {
|
||||
try {
|
||||
const token = process.env.BANXICO_TOKEN;
|
||||
if (!token) return res.status(400).json({ error: 'No se encontró token de Banxico' });
|
||||
|
||||
const today = formatDate(new Date());
|
||||
const url = `https://www.banxico.org.mx/SieAPIRest/service/v1/series/${BANXICO_SERIE}/datos/2021-11-07/${today}`;
|
||||
|
||||
const response = await axios.get(url, {
|
||||
headers: { 'Bmx-Token': token, 'Accept': 'application/json' }
|
||||
});
|
||||
|
||||
const datos = mapBanxicoData(response.data);
|
||||
|
||||
if (datos.length === 0) return res.status(404).json({ message: 'No hay datos para la fecha actual.' });
|
||||
|
||||
let savedCount = 0;
|
||||
for (const item of datos) {
|
||||
const result = await pool.query(
|
||||
`INSERT INTO exchangetype(fecha, valor)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (fecha) DO NOTHING`,
|
||||
[item.fecha, item.valor]
|
||||
);
|
||||
if (result.rowCount > 0) savedCount++;
|
||||
}
|
||||
|
||||
const message = savedCount > 0
|
||||
? 'Datos de tipo de cambio guardados correctamente'
|
||||
: 'No se pudieron guardar los datos (quizá ya existían)';
|
||||
|
||||
res.json({ message, savedCount, datos });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error consultExchange:', err.response ? err.response.data : err);
|
||||
res.status(500).json({ error: 'Error interno al consultar exchange' });
|
||||
}
|
||||
};
|
||||
|
||||
const getExchange = async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query('SELECT * FROM getexange()');
|
||||
const units = result.rows.map(u => ({
|
||||
id: u.id_exchange,
|
||||
fecha: u.fecha,
|
||||
dato: parseFloat(u.dato)
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvieron los tipos de cambio.",
|
||||
request: units
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo recuperar los tipos de cambio.' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { consultExchange, getExchange };
|
||||
699
backend/hotel_hacienda/src/controllers/expense.controller.js
Normal file
699
backend/hotel_hacienda/src/controllers/expense.controller.js
Normal file
@@ -0,0 +1,699 @@
|
||||
const pool = require('../db/connection');
|
||||
const transporter = require('../services/mailService');
|
||||
|
||||
const getPendingAppExpenses = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getpendingappexpenses()');
|
||||
|
||||
const status_approval = result.rows.map(pxp => ({
|
||||
id_expense: pxp.id_expense,
|
||||
expense_description: pxp.description,
|
||||
request_date: pxp.request_date,
|
||||
requested_by: pxp.name_user,
|
||||
area: pxp.name_area,
|
||||
amount: pxp.total,
|
||||
status_appr: pxp.status_approval
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:status_approval
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener gastos pendientes.' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getApprovedAppExpenses = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getapprovedappexpenses()');
|
||||
|
||||
const status_approval = result.rows.map(pxp => ({
|
||||
id_expense: pxp.id_expense,
|
||||
expense_description: pxp.description,
|
||||
request_date: pxp.request_date,
|
||||
approval_date: pxp.approval_date,
|
||||
requested_by: pxp.name_user,
|
||||
area: pxp.name_area,
|
||||
amount: pxp.total,
|
||||
status_appr: pxp.status_approval
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:status_approval
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener gastos aprobados.' });
|
||||
}
|
||||
|
||||
};
|
||||
const getRejectedappExpenses = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getrejectedappexpenses()');
|
||||
|
||||
const status_approval = result.rows.map(pxp => ({
|
||||
id_expense: pxp.id_expense,
|
||||
expense_description: pxp.description,
|
||||
request_date: pxp.request_date,
|
||||
reject_date: pxp.reject_date,
|
||||
requested_by: pxp.name_user,
|
||||
area: pxp.name_area,
|
||||
amount: pxp.total,
|
||||
status_appr: pxp.status_approval
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:status_approval
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener gastos rechazados.' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getTotalApproved = async (req, res) => {
|
||||
try{
|
||||
const {option} = req.body;
|
||||
if(option ==1 )
|
||||
{
|
||||
const result = await pool.query('SELECT totalApproved() as total');
|
||||
const total = result.rows[0].total;
|
||||
res.json({
|
||||
message: "Total aprobado",
|
||||
data:total
|
||||
});
|
||||
}
|
||||
else if (option ==2){
|
||||
const result = await pool.query('SELECT totalrejected() as total');
|
||||
const total = result.rows[0].total;
|
||||
res.json({
|
||||
message: "Total rechazado",
|
||||
data:total });
|
||||
}else {
|
||||
return res.status(400).json({
|
||||
message: "El parámetro 'option' solo puede ser 1 (aprobado) o 2 (rechazado).",
|
||||
data: 0
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el total de aprobados.' });
|
||||
}
|
||||
};
|
||||
const mainSupplier = async (req, res) => {
|
||||
try{
|
||||
|
||||
const result = await pool.query('SELECT * from mainsupplier()');
|
||||
const mainSupp = result.rows.map(ms => ({
|
||||
supplier: ms.name_suppliers,
|
||||
percentage: ms.percentage,
|
||||
}));
|
||||
res.json({
|
||||
message: "Proveedor Principal",
|
||||
data:mainSupp
|
||||
});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el total de aprobados.' });
|
||||
}
|
||||
};
|
||||
|
||||
const getReportExpenses = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM report_expenses()');
|
||||
|
||||
const reportExpenses = result.rows.map(re => ({
|
||||
id_expense: re.id_expense,
|
||||
expense_description: re.description,
|
||||
name_suppliers: re.name_suppliers,
|
||||
rfc_suppliers: re.rfc_suppliers,
|
||||
request_date: re.request_date,
|
||||
approval_date: re.approval_date,
|
||||
payment_date: re.payment_date,
|
||||
requested_by: re.requested_by,
|
||||
totalpesos: re.totalpesos,
|
||||
totaldolares: re.totaldolares,
|
||||
category: re.category,
|
||||
area: re.area,
|
||||
status_approval: re.approval_status,
|
||||
status_payment: re.payment_status,
|
||||
is_monthly : re.is_monthly
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:reportExpenses
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el reporte.' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getReportPayments = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM paymentsreport()');
|
||||
|
||||
const reportPayment = result.rows.map(rp => ({
|
||||
id_expense: rp.id_expense,
|
||||
expense_description: rp.description,
|
||||
approval_date: rp.approval_date,
|
||||
area: rp.area,
|
||||
supplier: rp.supplier,
|
||||
totalpesos: rp.totalpesos,
|
||||
totaldolares: rp.totaldolares,
|
||||
status_approval: rp.approval_status,
|
||||
status_payment: rp.payment_status
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:reportPayment
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el reporte de pagos.' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const countpending = async (req, res) => {
|
||||
try{
|
||||
const {option} = req.body;
|
||||
if(option ==1 ) //OBtienes cuantas aprobaciones tienes pendientes
|
||||
{
|
||||
const result = await pool.query('SELECT countpendingapprove() as countpen');
|
||||
const total = result.rows[0].countpen;
|
||||
res.json({
|
||||
message: "Pagos pendientes de aprobación",
|
||||
data:total
|
||||
});
|
||||
}else if(option ==2) //Obtienes los pagos pendientes de los gastos mensuales
|
||||
{
|
||||
const result = await pool.query('SELECT countpendingmonthly() as monthpen');
|
||||
const total = result.rows[0].monthpen;
|
||||
res.json({
|
||||
message: "Pagos mensuales pendientes",
|
||||
data:total
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el conteo de pendientes.' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getmonthlypayments = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getmonthlypayments()');
|
||||
|
||||
const monthlypayment = result.rows.map(mp => ({
|
||||
id_expense: mp.id_expense,
|
||||
expense_description: mp.description,
|
||||
recurrence_name: mp.recurrence_name,
|
||||
payment_type: mp.payment_type,
|
||||
payment_deadline: mp.payment_deadline,
|
||||
area: mp.name_area,
|
||||
supplier: mp.name_suppliers,
|
||||
status: mp.status,
|
||||
total: mp.total,
|
||||
status_payment: mp.name_pay_status
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:monthlypayment
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el reporte de pagos mensuales.' });
|
||||
}
|
||||
|
||||
};
|
||||
const getExpense = async(req,res) =>
|
||||
{
|
||||
const { id } = req.params;
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getexpense($1)',[parseInt(id)]);
|
||||
|
||||
const expense = result.rows.map(exp => ({
|
||||
id_exp: exp.id_exp,
|
||||
description_exp: exp.description_exp,
|
||||
suppliers_id: exp.suppliers_id,
|
||||
request_date: exp.request_date,
|
||||
payment_deadline: exp.payment_deadline,
|
||||
request_by: exp.request_by,
|
||||
approval_by: exp.approval_by,
|
||||
id_area: exp.id_area,
|
||||
category_exp: exp.category_exp,
|
||||
currency: exp.currency,
|
||||
subtotal: exp.subtotal,
|
||||
iva: exp.iva,
|
||||
ieps: exp.ieps,
|
||||
total: exp.total,
|
||||
needtoapprove: exp.needtoapprove,
|
||||
products: exp.products,
|
||||
}));
|
||||
res.json({
|
||||
data:expense
|
||||
});
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getInfoExpense = async(req,res) =>
|
||||
{
|
||||
try{
|
||||
const infoSuppliers = await pool.query('SELECT * FROM getsuppliers()');
|
||||
const infoCategories = await pool.query('SELECT * FROM getcategoryexpense()');
|
||||
const infoAreas = await pool.query('SELECT * FROM getareas()');
|
||||
const infoUsers = await pool.query('SELECT * FROM getapproveby()');
|
||||
const infoCurrencies = await pool.query('SELECT * FROM getcurrency()');
|
||||
const infoUniforms = await pool.query('SELECT * FROM getuniforms()');
|
||||
const taxes = await pool.query('SELECT * FROM gettaxes()');
|
||||
|
||||
const suppliers = infoSuppliers.rows.map(supp => ({
|
||||
id: supp.id_supplier,
|
||||
rfc: supp.rfc_suppliers,
|
||||
name: supp.name_suppliers
|
||||
}));
|
||||
const categories = infoCategories.rows.map(cat => ({
|
||||
id: cat.id_category,
|
||||
name: cat.name_category,
|
||||
spanish_name: cat.spanish_name
|
||||
}));
|
||||
const areas = infoAreas.rows.map(area => ({
|
||||
id: area.id_area,
|
||||
name: area.name_area,
|
||||
}));
|
||||
const users = infoUsers.rows.map(user => ({
|
||||
id: user.id_user,
|
||||
name: user.user_name,
|
||||
}));
|
||||
const currencies = infoCurrencies.rows.map(cur => ({
|
||||
id: cur.id_currency,
|
||||
name: cur.name_currency,
|
||||
}));
|
||||
const uniforms = infoUniforms.rows.map(uni => ({
|
||||
id: uni.id_uniform,
|
||||
name: uni.name_uniform
|
||||
}));
|
||||
const tax = taxes.rows.map(uni => ({
|
||||
id: uni.id_tax,
|
||||
name: uni.name_tax,
|
||||
number: uni.number
|
||||
}));
|
||||
|
||||
res.json({
|
||||
suppliers,
|
||||
categories,
|
||||
areas,
|
||||
users,
|
||||
currencies,
|
||||
uniforms,
|
||||
tax
|
||||
});
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener la información especifica.' });
|
||||
}
|
||||
};
|
||||
|
||||
const newExpense = async (req, res) => {
|
||||
try{
|
||||
const {new_description,
|
||||
suppliers_id,
|
||||
new_request_date,
|
||||
new_payment_deadline,
|
||||
request_by,
|
||||
area,
|
||||
expense_cat,
|
||||
currency_id,
|
||||
products,
|
||||
new_iva,
|
||||
new_ieps,
|
||||
new_subtotal,
|
||||
new_total,
|
||||
needtoapprove
|
||||
}= req.body;
|
||||
|
||||
const product = Array.isArray(products) && products.length > 0 ? JSON.stringify(products) : null;
|
||||
const result = await pool.query('SELECT newexpensev2($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb,$11,$12,$13,$14) AS STATUS',[
|
||||
new_description,suppliers_id,new_request_date,new_payment_deadline,request_by,
|
||||
area,expense_cat,currency_id,needtoapprove,product,new_subtotal,new_iva,new_ieps,new_total]);
|
||||
const userEmail = await pool.query('SELECT * FROM getusersmailowner()');
|
||||
|
||||
const newExpense = result.rows[0].status;
|
||||
const user = userEmail.rows;
|
||||
/*let fechaFormateada = 'No definida';
|
||||
if (new_payment_deadline) {
|
||||
try {
|
||||
const fecha = new Date(new_payment_deadline);
|
||||
fechaFormateada = isNaN(fecha)
|
||||
? (() => {
|
||||
const [dia, mes, anio] = new_payment_deadline.split('/');
|
||||
return new Date(`${anio}-${mes}-${dia}`).toLocaleDateString('en-US');
|
||||
})()
|
||||
: fecha.toLocaleDateString('en-US');
|
||||
} catch {
|
||||
fechaFormateada = 'Fecha inválida';
|
||||
}
|
||||
}*/
|
||||
const productosHTML = Array.isArray(products)
|
||||
? products.map(p => `<li>${p.id_product || 'N/A'} - QUANTITY: ${p.quantity || 1}</li>`).join('')
|
||||
: 'without products associate';
|
||||
const recipients = user.map(u => u.email);
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: recipients,
|
||||
subject: `Approval of the new expense ${newExpense}`,
|
||||
html: `<div style="max-width:600px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;
|
||||
background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;
|
||||
overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div style="background-color:#7a0c1e;padding:18px 22px;">
|
||||
<h2 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">
|
||||
Expenditure approval required
|
||||
</h2>
|
||||
<p style="margin:6px 0 0 0;color:#f3dcdc;font-size:13px;">
|
||||
Action pending on your part
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="padding:22px;color:#333333;font-size:14px;line-height:1.6;">
|
||||
<p style="margin-top:0;">
|
||||
An expense has been recorded that requires your approval.
|
||||
</p>
|
||||
|
||||
<div style="background-color:#faf5f6;border-left:4px solid #7a0c1e;
|
||||
padding:12px 14px;margin:16px 0;">
|
||||
<strong style="color:#7a0c1e;">ID EXPENSE:</strong>
|
||||
<span style="font-size:15px;">${newExpense}</span>
|
||||
</div>
|
||||
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">
|
||||
<tr>
|
||||
<td style="padding:6px 0;"><strong>Description:</strong></td>
|
||||
<td style="padding:6px 0;">${new_description}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:6px 0;"><strong>Deadline:</strong></td>
|
||||
<td style="padding:6px 0;">${new_payment_deadline}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:6px 0;"><strong>Total:</strong></td>
|
||||
<td style="padding:6px 0;font-weight:bold;color:#7a0c1e;">
|
||||
${new_total || 'N/A'}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div style="margin-top:18px;">
|
||||
<p style="margin-bottom:6px;"><strong>Products included:</strong></p>
|
||||
<ul style="padding-left:18px;margin:0;">
|
||||
${productosHTML}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div style="text-align:center;margin-top:32px;">
|
||||
<a href="https://hacienda.consultoria-as.com/"
|
||||
style="background-color:#7a0c1e;color:#ffffff;
|
||||
text-decoration:none;font-weight:bold;
|
||||
padding:14px 28px;border-radius:6px;
|
||||
display:inline-block;">
|
||||
Review and approve spending
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:16px;font-size:12px;color:#777777;text-align:center;">
|
||||
We recommend reviewing this application before the deadline.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="background-color:#f6f6f6;padding:12px 20px;
|
||||
font-size:12px;color:#888888;text-align:center;">
|
||||
Financial management system · Consultoría AS
|
||||
</div>
|
||||
|
||||
</div>`,
|
||||
};
|
||||
if(needtoapprove== true){
|
||||
await transporter.sendMail(mailOptions);
|
||||
};
|
||||
res.json({
|
||||
message: "Gasto agregado correctamente.",
|
||||
new_expense: newExpense
|
||||
});
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const updateExpense = async (req, res) => {
|
||||
try{
|
||||
|
||||
let message = '';
|
||||
const { id } = req.params;
|
||||
const {up_description,
|
||||
suppliers_id,
|
||||
up_req_date,
|
||||
up_deadline,
|
||||
up_currency,
|
||||
up_request_by,
|
||||
up_area,
|
||||
up_category,
|
||||
needtoapprove,
|
||||
up_subtotal,
|
||||
up_iva,
|
||||
up_ieps,
|
||||
up_total,
|
||||
products
|
||||
}= req.body;
|
||||
|
||||
const product = Array.isArray(products) && products.length > 0 ? JSON.stringify(products) : null;
|
||||
|
||||
const result = await pool.query('SELECT updateexpensev4($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15::jsonb) AS status',
|
||||
[parseInt(id),up_description,suppliers_id,up_req_date,up_deadline,up_currency,
|
||||
up_request_by,up_area,up_category,needtoapprove,up_subtotal,up_iva,up_ieps,up_total,product]);
|
||||
const userEmail = await pool.query('SELECT * FROM getusersmailowner()');
|
||||
|
||||
const user = userEmail.rows;
|
||||
const upExpense = result.rows[0].status;
|
||||
switch (upExpense) {
|
||||
case 0:
|
||||
message = 'El gasto ya fue pagado, no se puede actualizar';
|
||||
break;
|
||||
case 1:
|
||||
message = 'El gasto se actualizo correctamente';
|
||||
break;
|
||||
default:
|
||||
message = 'Error desconocido en la validación';
|
||||
};
|
||||
|
||||
/*let fechaFormateada = 'No definida';
|
||||
if (up_deadline) {
|
||||
try {
|
||||
// Manejo de formato "DD-MM-YYYY"
|
||||
if (typeof up_deadline === 'string' && up_deadline.includes('-')) {
|
||||
const [dia, mes, anio] = up_deadline.split('-');
|
||||
const fecha = new Date(`${anio}-${mes}-${dia}`);
|
||||
fechaFormateada = !isNaN(fecha)
|
||||
? fecha.toLocaleDateString('es-MX', { timeZone: 'UTC' })
|
||||
: 'Fecha inválida';
|
||||
} else {
|
||||
const fecha = new Date(up_deadline);
|
||||
fechaFormateada = !isNaN(fecha)
|
||||
? fecha.toLocaleDateString('es-MX', { timeZone: 'UTC' })
|
||||
: 'Fecha inválida';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error al formatear fecha:', err);
|
||||
fechaFormateada = 'Fecha inválida';
|
||||
}
|
||||
};*/
|
||||
const productosHTML = Array.isArray(products)
|
||||
? products.map(p => `<li>${p.id_product || 'N/A'} - Quantity: ${p.quantity || 1}</li>`).join('')
|
||||
: 'Without products associate';
|
||||
const recipients = user.map(u => u.email);
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: recipients,
|
||||
subject: `An updated ID expense approval is required: ${id}`,
|
||||
html: `<div style="max-width:600px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;
|
||||
background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;
|
||||
overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div style="background-color:#7a0c1e;padding:18px 22px;">
|
||||
<h2 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">
|
||||
Expenditure approval required
|
||||
</h2>
|
||||
<p style="margin:6px 0 0 0;color:#f3dcdc;font-size:13px;">
|
||||
Action pending on your part
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="padding:22px;color:#333333;font-size:14px;line-height:1.6;">
|
||||
<p style="margin-top:0;">
|
||||
An expense has been updated that requires your approval.
|
||||
</p>
|
||||
|
||||
<div style="background-color:#faf5f6;border-left:4px solid #7a0c1e;
|
||||
padding:12px 14px;margin:16px 0;">
|
||||
<strong style="color:#7a0c1e;">ID EXPENSE:</strong>
|
||||
<span style="font-size:15px;">${id}</span>
|
||||
</div>
|
||||
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">
|
||||
<tr>
|
||||
<td style="padding:6px 0;"><strong>Description:</strong></td>
|
||||
<td style="padding:6px 0;">${up_description}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:6px 0;"><strong>Deadline:</strong></td>
|
||||
<td style="padding:6px 0;">${up_deadline}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:6px 0;"><strong>Total:</strong></td>
|
||||
<td style="padding:6px 0;font-weight:bold;color:#7a0c1e;">
|
||||
${up_total || 'N/A'}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div style="margin-top:18px;">
|
||||
<p style="margin-bottom:6px;"><strong>Products included:</strong></p>
|
||||
<ul style="padding-left:18px;margin:0;">
|
||||
${productosHTML}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div style="text-align:center;margin-top:32px;">
|
||||
<a href="https://hacienda.consultoria-as.com/"
|
||||
style="background-color:#7a0c1e;color:#ffffff;
|
||||
text-decoration:none;font-weight:bold;
|
||||
padding:14px 28px;border-radius:6px;
|
||||
display:inline-block;">
|
||||
Review and approve spending
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:16px;font-size:12px;color:#777777;text-align:center;">
|
||||
We recommend reviewing this application before the deadline.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="background-color:#f6f6f6;padding:12px 20px;
|
||||
font-size:12px;color:#888888;text-align:center;">
|
||||
Financial management system · Consultoría AS
|
||||
</div>
|
||||
|
||||
</div>`,
|
||||
};
|
||||
if(needtoapprove== true){
|
||||
await transporter.sendMail(mailOptions);
|
||||
};
|
||||
|
||||
res.json({
|
||||
message,
|
||||
upExpense: upExpense
|
||||
});
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getTaxes = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM gettaxes()');
|
||||
|
||||
const taxes = result.rows.map(tx => ({
|
||||
id_tax: tx.id_tax,
|
||||
name_tax: tx.name_tax,
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:taxes
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los Taxes.' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const formatDateDMY = (value) => {
|
||||
if (!value) return '';
|
||||
const date = value instanceof Date ? value : new Date(value);
|
||||
if (isNaN(date.getTime())) return '';
|
||||
return date.toLocaleDateString('es-MX', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = { getPendingAppExpenses,getApprovedAppExpenses,countpending,
|
||||
getTotalApproved,mainSupplier,getReportExpenses,newExpense,
|
||||
getReportPayments,getmonthlypayments,getRejectedappExpenses,
|
||||
updateExpense,getExpense,getInfoExpense, getTaxes };
|
||||
161
backend/hotel_hacienda/src/controllers/hotelp.controller.js
Normal file
161
backend/hotel_hacienda/src/controllers/hotelp.controller.js
Normal file
@@ -0,0 +1,161 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const totalrevenue = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body
|
||||
const result = await pool.query('SELECT * FROM totalrevenue($1,$2) as status',[start_date,end_date]);
|
||||
const totalrenueve = result.rows[0].status;
|
||||
/*const totalrenueve = result.rows.map(re => ({
|
||||
total: re.total
|
||||
}));*/
|
||||
|
||||
|
||||
res.json({
|
||||
data:totalrenueve
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
const cogs = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body;
|
||||
const result = await pool.query('SELECT * FROM cogs($1,$2) as status',[start_date,end_date]);
|
||||
const cogs = result.rows[0].status;
|
||||
/*const cogs = result.rows.map(re => ({
|
||||
cogs_total: re.cogs_total,
|
||||
}));*/
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:cogs
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: error });
|
||||
}
|
||||
};
|
||||
|
||||
const employeeshare = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body
|
||||
const result = await pool.query('SELECT * FROM employeeshare($1,$2) as status ',[start_date,end_date]);
|
||||
const employeeshare = result.rows[0].status;
|
||||
/*const employeeshare = result.rows.map(re => ({
|
||||
total_share: re.total_share,
|
||||
}));*/
|
||||
res.json({
|
||||
data:employeeshare
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
const tips = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body
|
||||
const result = await pool.query('SELECT * FROM tips($1,$2) as status',[start_date,end_date]);
|
||||
const tips = result.rows[0].status;
|
||||
/*const tips = result.rows.map(re => ({
|
||||
tips: re.tips
|
||||
}));*/
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:tips
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
const grossprofit = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body;
|
||||
const result = await pool.query('SELECT * FROM grossprofit($1,$2) as status',[start_date,end_date]);
|
||||
const grossprofit = result.rows[0].status;
|
||||
/*const grossprofit = result.rows.map(re => ({
|
||||
grossprofit: re.grossprofit
|
||||
}));*/
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:grossprofit
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const weightedCategoriesCost = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body
|
||||
const result = await pool.query('SELECT * FROM weighted_categories_cost($1,$2)',[start_date,end_date]);
|
||||
const weightedCategoriesCost = result.rows.map(re => ({
|
||||
id_expense_cat : re.id_expense_cat ,
|
||||
category_name: re.category_name ,
|
||||
spanish_name : re.spanish_name ,
|
||||
total : re.total,
|
||||
participation : re.participation
|
||||
}));
|
||||
res.json({
|
||||
data:weightedCategoriesCost
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
const ebitda = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body;
|
||||
const result = await pool.query('SELECT * FROM ebitda($1,$2)', [start_date,end_date]);
|
||||
const ebitda = result.rows.map(re => ({
|
||||
expenses_total: re.expenses_total,
|
||||
ebitda : re.ebitda
|
||||
}));
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:ebitda
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {ebitda,weightedCategoriesCost,grossprofit,totalrevenue,cogs,employeeshare,tips};
|
||||
309
backend/hotel_hacienda/src/controllers/incomehrx.controller.js
Normal file
309
backend/hotel_hacienda/src/controllers/incomehrx.controller.js
Normal file
@@ -0,0 +1,309 @@
|
||||
const pool = require('../db/connection');
|
||||
const axios = require('axios');
|
||||
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
const getFacturas = async (initialDate, finalDate) => {
|
||||
try {
|
||||
const response = await axios.get(process.env.FACTURAS_API_URL, {
|
||||
params: {
|
||||
issuerRfc: process.env.FACTURAS_ISSUER_RFC,
|
||||
type: process.env.FACTURAS_TYPE,
|
||||
initialDate: initialDate,
|
||||
finalDate: finalDate
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.FACTURAS_API_TOKEN}`
|
||||
},
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
return response.data;
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Error API Facturas:',
|
||||
error.response?.data || error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
|
||||
};
|
||||
const addfacturas = async (req, res) => {
|
||||
try {
|
||||
const now = new Date();
|
||||
|
||||
const startOfYear = new Date(now.getFullYear(), 0, 1, 0, 0, 0);
|
||||
//console.log(startOfYear);
|
||||
const formatDateTime = (date) =>
|
||||
date.toISOString().slice(0, 19).replace('T', ' ');
|
||||
const FACTURAS_INITIAL_DATE = formatDateTime(startOfYear);
|
||||
const FACTURAS_FINAL_DATE = formatDateTime(now);
|
||||
console.log(FACTURAS_INITIAL_DATE);
|
||||
console.log(FACTURAS_FINAL_DATE);
|
||||
const facturas = await getFacturas(FACTURAS_INITIAL_DATE,FACTURAS_FINAL_DATE);
|
||||
|
||||
const result = await pool.query('SELECT addhoruxdata($1::jsonb) as total',[JSON.stringify(facturas)]);
|
||||
const total = result.rows[0].total;
|
||||
res.json({
|
||||
message: 'Facturas subidas a la base de datos',
|
||||
total
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al sincronizar' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const getCategoryIncome = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getcategoryincome()');
|
||||
|
||||
const categoriesIncome = result.rows.map(inc => ({
|
||||
id_cat_income: inc.id_cat_income,
|
||||
name_cat_income: inc.name_cat_income,
|
||||
english_name: inc.english_name
|
||||
}));
|
||||
res.json({
|
||||
data:categoriesIncome
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener las categorias' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getInvoiceIncome = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getinvoiceincome()');
|
||||
|
||||
const invoiceIncome = result.rows.map(inc => ({
|
||||
id_inv_income: inc.id_inv_income,
|
||||
name_inv_income: inc.name_inv_income,
|
||||
amount: inc.amount,
|
||||
dateinvoice: inc.dateinvoice
|
||||
|
||||
}));
|
||||
res.json({
|
||||
data:invoiceIncome
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener las facturas.' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getAccountIncome = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getaccountincome()');
|
||||
|
||||
const aacountIncome = result.rows.map(inc => ({
|
||||
id_acc_income: inc.id_acc_income,
|
||||
name_acc_income: inc.name_acc_income,
|
||||
}));
|
||||
res.json({
|
||||
data:aacountIncome
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener las cuentas' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getIncomeHorux = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getincomehorux()');
|
||||
|
||||
const incomeHorux = result.rows.map(inc => ({
|
||||
id_hrx_income: inc.id_hrx_income,
|
||||
date_in: inc.date_in,
|
||||
createddate: inc.createddate,
|
||||
account_name: inc.account_name,
|
||||
categories: inc.categories,
|
||||
amount:inc.amount,
|
||||
amountinvoice: inc.amountinvoice,
|
||||
invoice: inc.invoice,
|
||||
status_in: inc.status_in
|
||||
}));
|
||||
res.json({
|
||||
data:incomeHorux
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los income' });
|
||||
}
|
||||
|
||||
};
|
||||
const getTotalIncome = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM gettotalincome() as total');
|
||||
|
||||
const totalIncome = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: 'Total de income',
|
||||
data: totalIncome
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los income' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const newIncome = async (req, res) => {
|
||||
try{
|
||||
const {
|
||||
account_id,
|
||||
amount,
|
||||
new_date,
|
||||
newinvoice,
|
||||
area_id,
|
||||
categories,
|
||||
|
||||
} = req.body;
|
||||
const categoriesJson = categories ? JSON.stringify(categories) : '[]';
|
||||
|
||||
const result = await pool.query('SELECT newincome($1,$2,$3,$4,$5,$6::jsonb) AS status',[
|
||||
account_id,amount,new_date,newinvoice,area_id,categoriesJson]);
|
||||
const newIncome = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Income agregado correctamente",
|
||||
new_expense: newIncome
|
||||
});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getOneIncome = async (req, res) => {
|
||||
try{
|
||||
const { id } = req.params;
|
||||
const result = await pool.query('SELECT * FROM getoneincome($1)',[parseInt(id)]);
|
||||
|
||||
const incomeHorux = result.rows.map(inc => ({
|
||||
id_hrx_income: inc.id_hrx_income,
|
||||
date_in: inc.date_in,
|
||||
account_id: inc.account_id,
|
||||
amount:inc.amount,
|
||||
invoice: inc.invoice,
|
||||
categories: inc.categories
|
||||
}));
|
||||
res.json({
|
||||
data:incomeHorux
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el income' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const updateIncome = async (req, res) => {
|
||||
try{
|
||||
const {id} = req.params;
|
||||
const {
|
||||
account_id,
|
||||
amount,
|
||||
up_date,
|
||||
up_invoice,
|
||||
area_id,
|
||||
categories
|
||||
} = req.body;
|
||||
const categoriesJson = categories ? JSON.stringify(categories) : '[]';
|
||||
|
||||
const result = await pool.query('SELECT updateincome($1,$2,$3,$4,$5,$6,$7::jsonb) AS status',[
|
||||
id,account_id,amount,up_date,up_invoice,area_id,categoriesJson]);
|
||||
const newIncome = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Income actualizado correctamente",
|
||||
new_expense: newIncome
|
||||
});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
|
||||
};
|
||||
const getstripeservice = async (req, res) => {
|
||||
try{
|
||||
/*const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount: 4805510,
|
||||
currency: 'usd',
|
||||
payment_method: 'pm_card_visa',
|
||||
confirm: true,
|
||||
automatic_payment_methods: {
|
||||
enabled: true,
|
||||
allow_redirects: 'never'
|
||||
}
|
||||
});*/
|
||||
const balanceTransactions = await stripe.transfers.list();
|
||||
const result = await pool.query(
|
||||
'SELECT addstripedatav2($1::jsonb) AS inserted',
|
||||
[JSON.stringify(balanceTransactions)]);
|
||||
res.json({
|
||||
message: "Se obtuvieron datos de stripe y se insertaron",
|
||||
transactions: result.rows[0].inserted,
|
||||
//data: balanceTransactions
|
||||
});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
const addDemostripeData = async (req, res) => {
|
||||
try{
|
||||
const transfer = await stripe.transfers.create({
|
||||
amount: 1000, // centavos
|
||||
currency: 'usd',
|
||||
destination: 'acct_1Sm54nEzzXSLq34b',
|
||||
transfer_group: 'ORDER_95',});
|
||||
res.json({
|
||||
message: 'Pago creado y confirmado',
|
||||
transfer
|
||||
});
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = {getCategoryIncome,getInvoiceIncome,getAccountIncome,
|
||||
getIncomeHorux,getTotalIncome,newIncome,getOneIncome,updateIncome,getFacturas,addfacturas,getstripeservice,
|
||||
addDemostripeData};
|
||||
514
backend/hotel_hacienda/src/controllers/incomes.controller.js
Normal file
514
backend/hotel_hacienda/src/controllers/incomes.controller.js
Normal file
@@ -0,0 +1,514 @@
|
||||
const pool = require('../db/connection');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const XLSX = require('xlsx');
|
||||
const csv = require('csv-parser');
|
||||
const getincomes = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {checkin_initial,checkin_final} = req.body;
|
||||
const result = await pool.query('SELECT * FROM getincomesv2($1,$2)',[checkin_initial,checkin_final]);
|
||||
const incomereport = result.rows.map(re => ({
|
||||
room: re.room,
|
||||
occupation: re.occupation,
|
||||
income_per_night: re.income_per_night,
|
||||
total_income: re.total_income
|
||||
}));
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:incomereport
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos.' });
|
||||
}
|
||||
};
|
||||
|
||||
const getSoftRestaurant = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
//const {checkin_initial,checkin_final} = req.body;
|
||||
const result = await pool.query('SELECT * FROM getdataproductsolds()',);
|
||||
const incomereport = result.rows.map(re => ({
|
||||
id_soldpro: re.id_soldpro,
|
||||
clave: re.clave,
|
||||
description: re.description,
|
||||
group_sp: re.group_sp,
|
||||
price: re.price,
|
||||
quantity: re.quantity,
|
||||
totalsale: re.totalsale,
|
||||
cost_sp: re.cost_sp,
|
||||
totalcost: re.totalcost,
|
||||
salecost: re.salecost,
|
||||
catalogprice: re.catalogprice,
|
||||
totalsalecatalogprice: re.totalsalecatalogprice,
|
||||
vat_rate: re.vat_rate
|
||||
}));
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:incomereport
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos.' });
|
||||
}
|
||||
};
|
||||
|
||||
const getdetallecheques = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
//const {checkin_initial,checkin_final} = req.body;
|
||||
const result = await pool.query('SELECT * FROM getchequesdetalles()',);
|
||||
const incomereport = result.rows.map(re => ({
|
||||
id_cheque: re.id_cheque,
|
||||
folio: re.folio,
|
||||
fecha: re.fecha,
|
||||
descuento: re.descuento,
|
||||
importe: re.importe,
|
||||
cargo: re.cargo,
|
||||
efectivo: re.efectivo,
|
||||
tarjeta: re.tarjeta,
|
||||
vales: re.vales,
|
||||
propina: re.propina,
|
||||
OTROS: re.OTROS,
|
||||
}));
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:incomereport
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos.' });
|
||||
}
|
||||
};
|
||||
|
||||
const totalincomes = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {checkin_initial,checkin_final} = req.body;
|
||||
const result = await pool.query('SELECT totalincomesv2($1,$2) as total',[checkin_initial,checkin_final]);
|
||||
const totalincomes = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos de las fechas seleccionadas",
|
||||
data:totalincomes
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos totales.' });
|
||||
}
|
||||
};
|
||||
|
||||
const channelscards = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {checkin_initial,checkin_final} = req.body;
|
||||
const result = await pool.query('SELECT * from channelscardsv2($1,$2)',[checkin_initial,checkin_final]);
|
||||
const incomef = result.rows.map(re => ({
|
||||
channel: re.channel,
|
||||
total_channel: re.total_channel,
|
||||
percentage: re.percentage,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
message: "Canal donde se obtuvieron más ingresos",
|
||||
data:incomef
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
const loadincomes = (req, res) => {
|
||||
const {nombrearchivo} = req.body;
|
||||
const CSV_PATH = `/home/Hotel/backend/hotel_hacienda/src/resources/littleHotelier/${nombrearchivo}`;
|
||||
//const CSV_PATH = `C:/Users/otamegane/Desktop/Trabajo_free_lancer/reservations/${nombrearchivo}`;
|
||||
const results = [];
|
||||
|
||||
if (!fs.existsSync(CSV_PATH)) {
|
||||
return res.status(404).json({
|
||||
message: 'El archivo CSV no existe'
|
||||
});
|
||||
}
|
||||
|
||||
fs.createReadStream(CSV_PATH)
|
||||
.pipe(csv())
|
||||
.on('data', (row) => {
|
||||
results.push(row);
|
||||
})
|
||||
.on('end', async () => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT insertincomesandtempV2($1::jsonb) AS inserted',
|
||||
[JSON.stringify(results)]
|
||||
);
|
||||
|
||||
res.json({
|
||||
message: 'CSV procesado e insertado en base de datos',
|
||||
rows_read: results.length,
|
||||
rows_inserted: result.rows[0].inserted
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error insertando en BD:', error);
|
||||
res.status(500).json({ message: 'Error al insertar datos en BD' });
|
||||
}
|
||||
})
|
||||
.on('error', (err) => {
|
||||
console.error('Error leyendo el CSV:', err);
|
||||
res.status(500).json({ error: 'No se pudo leer el CSV' });
|
||||
});
|
||||
};
|
||||
|
||||
/*const loadsoftrestaurant = (req, res) => {
|
||||
const {nombrearchivo} = req.body;
|
||||
//const CSV_PATH = `/home/Hotel/backend/hotel_hacienda/src/resources/littleHotelier/${nombrearchivo}`;
|
||||
const CSV_PATH = `C:/Users/otamegane/Desktop/Trabajo_free_lancer/reservations/${nombrearchivo}`;
|
||||
const results = [];
|
||||
|
||||
if (!fs.existsSync(CSV_PATH)) {
|
||||
return res.status(404).json({
|
||||
message: 'El archivo CSV no existe'
|
||||
});
|
||||
}
|
||||
|
||||
fs.createReadStream(CSV_PATH)
|
||||
.pipe(csv())
|
||||
.on('data', (row) => {
|
||||
results.push(row);
|
||||
})
|
||||
.on('end', async () => {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
'SELECT insertproductsale($1::jsonb) AS inserted',
|
||||
[JSON.stringify(results)]
|
||||
);
|
||||
|
||||
res.json({
|
||||
message: 'CSV procesado e insertado en base de datos',
|
||||
rows_read: results.length,
|
||||
rows_inserted: result.rows[0].inserted
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error insertando en BD:', error);
|
||||
res.status(500).json({ message: 'Error al insertar datos en BD' });
|
||||
}
|
||||
})
|
||||
.on('error', (err) => {
|
||||
console.error('Error leyendo el CSV:', err);
|
||||
res.status(500).json({ error: 'No se pudo leer el CSV' });
|
||||
});
|
||||
};*/
|
||||
|
||||
const loadsoftrestaurant = async (req, res) => {
|
||||
try {
|
||||
const { nombrearchivo } = req.body;
|
||||
//const FILE_PATH = `C:/Users/otamegane/Desktop/Trabajo_free_lancer/reservations/${nombrearchivo}`;
|
||||
const FILE_PATH = `/home/Hotel/backend/hotel_hacienda/src/resources/littleHotelier/${nombrearchivo}`;
|
||||
if (!fs.existsSync(FILE_PATH)) {
|
||||
return res.status(404).json({
|
||||
message: 'El archivo Excel no existe'
|
||||
});
|
||||
}
|
||||
|
||||
//Se lee el archivo
|
||||
const workbook = XLSX.readFile(FILE_PATH);
|
||||
|
||||
//Toma la primera hoja primera hoja
|
||||
const sheetName = workbook.SheetNames[0];
|
||||
const worksheet = workbook.Sheets[sheetName];
|
||||
|
||||
//Se convierte a json
|
||||
const results = XLSX.utils.sheet_to_json(worksheet, {
|
||||
defval: null,
|
||||
range: 4
|
||||
});
|
||||
|
||||
//console.log(results[0]);
|
||||
|
||||
if (results.length === 0) {
|
||||
return res.status(400).json({
|
||||
message: 'El archivo no contiene datos'
|
||||
});
|
||||
}
|
||||
|
||||
// Insertar en PostgreSQL
|
||||
const result = await pool.query(
|
||||
'SELECT insertproductsale($1::jsonb) AS inserted',
|
||||
[JSON.stringify(results)]
|
||||
);
|
||||
|
||||
res.json({
|
||||
message: 'Excel procesado e insertado en base de datos',
|
||||
rows_read: results.length,
|
||||
rows_inserted: result.rows[0].inserted
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error procesando Excel:', error);
|
||||
res.status(500).json({
|
||||
message: 'Error al procesar el archivo Excel'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const loadChequesDetalle = async (req, res) => {
|
||||
try {
|
||||
const { nombrearchivo } = req.body;
|
||||
const FILE_PATH = `/home/Hotel/backend/hotel_hacienda/src/resources/littleHotelier/${nombrearchivo}`;
|
||||
//const FILE_PATH = `C:/Users/otamegane/Desktop/Trabajo_free_lancer/reservations/${nombrearchivo}`;
|
||||
|
||||
if (!fs.existsSync(FILE_PATH)) {
|
||||
return res.status(404).json({
|
||||
message: 'El archivo Excel no existe'
|
||||
});
|
||||
}
|
||||
|
||||
//Se lee el archivo
|
||||
const workbook = XLSX.readFile(FILE_PATH);
|
||||
|
||||
//Toma la primera hoja primera hoja
|
||||
const sheetName = workbook.SheetNames[0];
|
||||
const worksheet = workbook.Sheets[sheetName];
|
||||
|
||||
//Se convierte a json
|
||||
const results = XLSX.utils.sheet_to_json(worksheet, {
|
||||
defval: null,
|
||||
range: 5
|
||||
});
|
||||
|
||||
//console.log(results[0]);
|
||||
|
||||
if (results.length === 0) {
|
||||
return res.status(400).json({
|
||||
message: 'El archivo no contiene datos'
|
||||
});
|
||||
}
|
||||
|
||||
// Insertar en PostgreSQL
|
||||
const result = await pool.query(
|
||||
'SELECT insertchequesdetalles($1::jsonb) AS inserted',
|
||||
[JSON.stringify(results)]
|
||||
);
|
||||
|
||||
res.json({
|
||||
message: 'Excel procesado e insertado en base de datos',
|
||||
rows_read: results.length,
|
||||
rows_inserted: result.rows[0].inserted
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error procesando Excel:', error);
|
||||
res.status(500).json({
|
||||
message: 'Error al procesar el archivo Excel'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const reportIncomes = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT * from stadisticsrooms($1,$2)',[initial_date,final_date]);
|
||||
const reportincome = result.rows.map(re => ({
|
||||
room_type: re.room_type,
|
||||
total_uses: re.total_uses,
|
||||
total_income: re.total_income,
|
||||
occupation_percentage: re.occupation_percentage,
|
||||
income_percentage: re.income_percentage,
|
||||
ADR: re.adr,
|
||||
CPOR: re.cpor,
|
||||
CPAR: re.cpar,
|
||||
PPOR: re.ppor,
|
||||
fixed_cost: re.fixed_cost,
|
||||
break_event_point: re.break_event_point,
|
||||
gift_room: re.gift_room
|
||||
}));
|
||||
|
||||
res.json({
|
||||
message: "Reporte del income",
|
||||
data:reportincome
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato',
|
||||
error});
|
||||
}
|
||||
};
|
||||
|
||||
const countFolios = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT countFolios($1,$2) as total',[initial_date,final_date]);
|
||||
const count = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el conteo de los tickets",
|
||||
data:count
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos totales.' });
|
||||
}
|
||||
};
|
||||
|
||||
const sumasTotales = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT sumasTotales($1,$2) as total',[initial_date,final_date]);
|
||||
const sumatotal = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el total",
|
||||
data:sumatotal
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos totales.' });
|
||||
}
|
||||
};
|
||||
|
||||
const sumaEfectivo = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT sumaEfectivo($1,$2) as total',[initial_date,final_date]);
|
||||
const efectivo = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el total de efectivo",
|
||||
data:efectivo
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos efectivo.' });
|
||||
}
|
||||
};
|
||||
|
||||
const sumaTarjeta = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT sumaTarjeta($1,$2) as total',[initial_date,final_date]);
|
||||
const tarjeta = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el total de tarjeta",
|
||||
data:tarjeta
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos tarjeta.' });
|
||||
}
|
||||
};
|
||||
|
||||
const sumaVales = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT sumaVales($1,$2) as total',[initial_date,final_date]);
|
||||
const vales = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el total de vales",
|
||||
data:vales
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos vales.' });
|
||||
}
|
||||
};
|
||||
|
||||
const sumaOtros = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT sumaOtros($1,$2) as total',[initial_date,final_date]);
|
||||
const otros = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el total de otros",
|
||||
data:otros
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos otros.' });
|
||||
}
|
||||
};
|
||||
|
||||
const sumaPropinas = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT sumaPropinas($1,$2) as total',[initial_date,final_date]);
|
||||
const propinas = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el total de propinas",
|
||||
data:propinas
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los ingresos propinas.' });
|
||||
}
|
||||
};
|
||||
|
||||
const ticketPromedio = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {initial_date,final_date} = req.body;
|
||||
const result = await pool.query('SELECT ticketPromedio($1,$2) as total',[initial_date,final_date]);
|
||||
const promedio = result.rows[0].total;
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvo el promedio de ticket",
|
||||
data:promedio
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el promedio.' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {getincomes,totalincomes,loadChequesDetalle,getdetallecheques,
|
||||
channelscards,loadincomes,reportIncomes,loadsoftrestaurant,getSoftRestaurant,
|
||||
ticketPromedio,sumaEfectivo,sumaOtros,sumaPropinas,sumaTarjeta,sumaVales,sumasTotales,countFolios};
|
||||
650
backend/hotel_hacienda/src/controllers/mail.controller.js
Normal file
650
backend/hotel_hacienda/src/controllers/mail.controller.js
Normal file
@@ -0,0 +1,650 @@
|
||||
const pool = require('../db/connection');
|
||||
const transporter = require('../services/mailService');
|
||||
|
||||
const contractsNearToEnd = async (req, res) => {
|
||||
try{
|
||||
const countResult = await pool.query('SELECT contratcsneartoend() as total');
|
||||
const contractsResult = await pool.query('SELECT * FROM contractsneartoenddata()');
|
||||
const userEmails = await pool.query('SELECT * FROM getusersmailneartoend()');
|
||||
const totalExpiring = countResult.rows[0].total;
|
||||
const contracts = contractsResult.rows;
|
||||
const users = userEmails.rows;
|
||||
|
||||
if(totalExpiring >= 1 && users.length > 0){
|
||||
|
||||
const recipients = users.map(u => u.email);
|
||||
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: recipients,
|
||||
subject: 'Contratos próximos a expirar',
|
||||
html: `
|
||||
<div style="max-width:600px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;
|
||||
background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;
|
||||
overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div style="background-color:#7a0c1e;padding:18px 22px;">
|
||||
<h2 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">
|
||||
Notificación de contratos
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="padding:22px;color:#333333;font-size:14px;line-height:1.6;">
|
||||
<p style="margin-top:0;">
|
||||
Actualmente hay <b>${totalExpiring}</b> contrato(s) por expirar.
|
||||
</p>
|
||||
<p style="margin-top:0;">
|
||||
Los siguientes contratos son los que expiran:
|
||||
</p>
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border:1px solid #ddd;">
|
||||
<thead>
|
||||
<tr style="background-color:#f4f4f4;">
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">ID contract</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">RFC</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Nombre</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Fecha inicio contrato</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Fecha fin contrato</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Salario diario</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${contracts.map(c => {
|
||||
const inicio = formatDateDMY(c.start_contract);
|
||||
const fin = formatDateDMY(c.end_contract);
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.id_contract}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.rfc_emp}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.name_employee}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${inicio}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${fin}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.dailypay}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- CTA -->
|
||||
<div style="text-align:center;margin-top:32px;">
|
||||
<a href="https://hacienda.consultoria-as.com/"
|
||||
style="background-color:#7a0c1e;color:#ffffff;
|
||||
text-decoration:none;font-weight:bold;
|
||||
padding:14px 28px;border-radius:6px;
|
||||
display:inline-block;">
|
||||
Revisar en la web
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:16px;font-size:12px;color:#777777;text-align:center;">
|
||||
Por favor considere si seran recontratados o finalizados.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="background-color:#f6f6f6;padding:12px 20px;
|
||||
font-size:12px;color:#888888;text-align:center;">
|
||||
Sistema de gestión financiera · Consultoría AS
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`
|
||||
};
|
||||
await transporter.sendMail(mailOptions);
|
||||
res.json({
|
||||
message: 'Correo enviado con contratos por expirar.',
|
||||
data:totalExpiring
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: 'No hay contratos por expirar',
|
||||
data:totalExpiring
|
||||
});
|
||||
}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo mandar el correo con los contratos por expirar' });
|
||||
}
|
||||
};
|
||||
const paymentDeadLine = async (req, res) => {
|
||||
try{
|
||||
const deadLineCount = await pool.query('SELECT countdelaypayments() as total');
|
||||
const deadLineResult = await pool.query('SELECT * FROM getdelaypayments()');
|
||||
const usersDeadLine = await pool.query('SELECT * FROM getusersmailpaymentdelay()');
|
||||
const totalDeadline = deadLineCount.rows[0].total;
|
||||
const deadLines = deadLineResult.rows;
|
||||
const users = usersDeadLine.rows;
|
||||
|
||||
if(totalDeadline >= 1 && users.length >0){
|
||||
const recipients = users.map(u => u.email);
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: recipients,
|
||||
subject: 'WARNING: OVERDUE PAYMENTS',
|
||||
html: `
|
||||
<div style="max-width:600px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;
|
||||
background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;
|
||||
overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div style="background-color:#7a0c1e;padding:18px 22px;">
|
||||
<h2 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">
|
||||
Notification regarding overdue payments
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="padding:22px;color:#333333;font-size:14px;line-height:1.6;">
|
||||
<p style="margin-top:0;">
|
||||
Currently there are <b>${totalDeadline}</b> overdue payment(s).
|
||||
</p>
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border:1px solid #ddd;">
|
||||
<thead>
|
||||
<tr style="background-color:#f4f4f4;">
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">ID</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Description</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Deadline</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${deadLines.map(c => {
|
||||
const fecha = new Date(c.payment_deadline);
|
||||
const fechaFormateada = fecha.toLocaleDateString('en-US', {
|
||||
day: '2-digit',
|
||||
month: 'long'
|
||||
});
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.id_expense}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.description}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${fechaFormateada}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.total || 'N/A'}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- CTA -->
|
||||
<div style="text-align:center;margin-top:32px;">
|
||||
<a href="https://hacienda.consultoria-as.com/"
|
||||
style="background-color:#7a0c1e;color:#ffffff;
|
||||
text-decoration:none;font-weight:bold;
|
||||
padding:14px 28px;border-radius:6px;
|
||||
display:inline-block;">
|
||||
Please review and pay as soon as possible.
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:16px;font-size:12px;color:#777777;text-align:center;">
|
||||
We recommend that review your overdue expenses as soon as possible.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="background-color:#f6f6f6;padding:12px 20px;
|
||||
font-size:12px;color:#888888;text-align:center;">
|
||||
Financial management system · Consultoría AS
|
||||
</div>
|
||||
|
||||
</div>`
|
||||
};
|
||||
await transporter.sendMail(mailOptions);
|
||||
res.json({
|
||||
message: 'Correo enviado con pagos atrasados.',
|
||||
data:totalDeadline
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: 'No hay pagos con atraso',
|
||||
data:totalDeadline
|
||||
});
|
||||
}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo mandar el correo con con los pagos atrasados' });
|
||||
}
|
||||
};
|
||||
|
||||
const expensesNearToDeadline = async (req, res) => {
|
||||
try{
|
||||
const deadLineCount = await pool.query('SELECT COUNT(*) FROM expensesneartodeadline()');
|
||||
const deadLineResult = await pool.query('SELECT * FROM expensesneartodeadline()');
|
||||
const usersDeadLine = await pool.query('SELECT * FROM getusersmailpaymentdelay()');
|
||||
|
||||
const totalDeadline = deadLineCount.rows[0].count;
|
||||
const deadLines = deadLineResult.rows;
|
||||
const users = usersDeadLine.rows;
|
||||
console.log(totalDeadline);
|
||||
if(totalDeadline >= 1 && users.length >0){
|
||||
const recipients = users.map(u => u.email);
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: recipients,
|
||||
subject: 'WARNING: PAYMENTS DUE SOON FOR LATE PAYMENTS',
|
||||
html: `
|
||||
<div style="max-width:600px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;
|
||||
background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;
|
||||
overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div style="background-color:#7a0c1e;padding:18px 22px;">
|
||||
<h2 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">
|
||||
Notification of payments approaching a deadline
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="padding:22px;color:#333333;font-size:14px;line-height:1.6;">
|
||||
<p style="margin-top:0;">
|
||||
Currently there are <b>${totalDeadline}</b> payment(s) close to deadline.
|
||||
</p>
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border:1px solid #ddd;">
|
||||
<thead>
|
||||
<tr style="background-color:#f4f4f4;">
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">ID</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Description</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Deadline</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${deadLines.map(c => {
|
||||
const fecha = new Date(c.payment_deadline);
|
||||
const fechaFormateada = fecha.toLocaleDateString('en-US', {
|
||||
day: '2-digit',
|
||||
month: 'long'
|
||||
});
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.id_expense}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.description}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${fechaFormateada}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.total || 'N/A'}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- CTA -->
|
||||
<div style="text-align:center;margin-top:32px;">
|
||||
<a href="https://hacienda.consultoria-as.com/"
|
||||
style="background-color:#7a0c1e;color:#ffffff;
|
||||
text-decoration:none;font-weight:bold;
|
||||
padding:14px 28px;border-radius:6px;
|
||||
display:inline-block;">
|
||||
Review and pay spendings
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:16px;font-size:12px;color:#777777;text-align:center;">
|
||||
We recommend reviewing this application before the deadline.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="background-color:#f6f6f6;padding:12px 20px;
|
||||
font-size:12px;color:#888888;text-align:center;">
|
||||
Financial management system · Consultoría AS
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`
|
||||
};
|
||||
await transporter.sendMail(mailOptions);
|
||||
res.json({
|
||||
message: 'Correo enviado con pagos por expirar.',
|
||||
data:totalDeadline
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: 'No hay pagos con atraso',
|
||||
data:totalDeadline
|
||||
});
|
||||
}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo mandar el correo con los pagos prontos a fecha limite' });
|
||||
}
|
||||
};
|
||||
|
||||
const expensesspecial = async (req, res) => {
|
||||
try{
|
||||
const expensesCount = await pool.query('SELECT COUNT(*) AS total FROM getexpensesspecial()');
|
||||
const expensesResult = await pool.query('SELECT * FROM getexpensesspecial()');
|
||||
const totalExpenses = expensesCount.rows[0].total;
|
||||
const expenses = expensesResult.rows;
|
||||
|
||||
if(totalExpenses >= 1){
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: 'soporte@horuxfin.com',
|
||||
subject: 'Recordatorio: Hay gastos por aprobar',
|
||||
html: `
|
||||
<h2>Notificación de gastos por aprobar</h2>
|
||||
<p>Actualmente hay <b>${totalExpenses}</b> gasto(s) por aprobar</p>
|
||||
<ul>Los siguientes gastos faltan por aprobar</ul>
|
||||
<ul>
|
||||
${expenses.map(c => {
|
||||
const fecha = new Date(c.payment_deadline);
|
||||
const fechaFormateada = fecha.toLocaleDateString('es-MX');
|
||||
const product = Array.isArray(c.products) && c.products.length > 0
|
||||
? c.products.map(p => `${p.name_product} ($${p.total})`).join(', ')
|
||||
: 'N/A';
|
||||
return `<li>
|
||||
ID:${c.id_expense} - Descripción: ${c.description}
|
||||
- Productos: ${product} -
|
||||
fecha limite de pago: ${fechaFormateada} -
|
||||
total del gasto: ${c.total || 'N/A'}
|
||||
</li>`;
|
||||
}).join('')}
|
||||
</ul>`
|
||||
};
|
||||
await transporter.sendMail(mailOptions);
|
||||
res.json({
|
||||
message: 'Correo enviado con gastos por aprobar',
|
||||
data:totalExpenses
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: 'No hay pagos por aprobar',
|
||||
data:totalExpenses
|
||||
});
|
||||
}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo mandar el correo con los contratos por expirar' });
|
||||
}
|
||||
};
|
||||
|
||||
const emailbirthday = async (req, res) => {
|
||||
try{
|
||||
const endpointName = 'emailbirthday';
|
||||
const current = new Date();
|
||||
const mes = current.getMonth() + 1;
|
||||
const anio = current.getFullYear();
|
||||
const validateEndpoint = await pool.query('SELECT validateendpoint($1,$2::int,$3::int) AS total',[endpointName,mes,anio]);
|
||||
|
||||
if (validateEndpoint.rows[0].total > 0) {
|
||||
return res.status(400).json({
|
||||
message: `Este endpoint ya fue ejecutado en ${current.toLocaleString('es-MX', { month: 'long' })}.`
|
||||
});
|
||||
}else {
|
||||
await pool.query(`INSERT INTO endpoint_logs (endpoint_name) VALUES ($1)`,[endpointName]);
|
||||
const birthdayCount = await pool.query('SELECT COUNT(*) AS total FROM getbirthdaysemployees()');
|
||||
const birthdayResult = await pool.query('SELECT * FROM getbirthdaysemployees()');
|
||||
const userEmails = await pool.query('SELECT * FROM getusersmailbirthday()');
|
||||
|
||||
const totalBirthday = birthdayCount.rows[0].total;
|
||||
const birthdays = birthdayResult.rows;
|
||||
const users = userEmails.rows;
|
||||
const currentMonth = new Date().toLocaleString('en-US', { month: 'long' });
|
||||
|
||||
if(totalBirthday >= 1 && users.length >0){
|
||||
|
||||
const recipients = users.map(u => u.email);
|
||||
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: recipients,
|
||||
subject: `Friendly reminder: Birthdays of the month of ${currentMonth}`,
|
||||
html: `
|
||||
<div style="max-width:600px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;
|
||||
background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;
|
||||
overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div style="background-color:#7a0c1e;padding:18px 22px;">
|
||||
<h2 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">
|
||||
In this month there are ${totalBirthday} birthday person(s)
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="padding:22px;color:#333333;font-size:14px;line-height:1.6;">
|
||||
<p style="margin-top:0;">
|
||||
The following employees are celebrating their birthdays this month:
|
||||
</p>
|
||||
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border:1px solid #ddd;">
|
||||
<thead>
|
||||
<tr style="background-color:#f4f4f4;">
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">RFC</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Name</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Birthday</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${birthdays.map(c => {
|
||||
const fecha = new Date(c.birthday);
|
||||
const fechaFormateada = fecha.toLocaleDateString('en-US', {
|
||||
day: '2-digit',
|
||||
month: 'long'
|
||||
});
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.rfc}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.fullname}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${fechaFormateada}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p style="margin-top:16px;font-size:12px;color:#777777;text-align:center;">
|
||||
Remember to congratulate them on their special day!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="background-color:#f6f6f6;padding:12px 20px;
|
||||
font-size:12px;color:#888888;text-align:center;">
|
||||
Financial management system · Consultoría AS
|
||||
</div>
|
||||
|
||||
</div>`};
|
||||
await transporter.sendMail(mailOptions);
|
||||
res.json({
|
||||
message: 'Correo enviado con los cumpleaños',
|
||||
data:totalBirthday
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: 'No hay cumpleañeros este mes',
|
||||
data:totalBirthday
|
||||
});
|
||||
}}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo enviar correo con los cumpleañeros' });
|
||||
}
|
||||
};
|
||||
const expiredcontracts = async (req, res) => {
|
||||
try{
|
||||
const expiredCountTotal = await pool.query('SELECT expiredcontracts() as total');
|
||||
const expiredResult = await pool.query('SELECT * FROM getexpirecontracts()');
|
||||
const expiredCountDay = await pool.query('SELECT COUNT(*) AS total FROM getexpirecontracts()');
|
||||
const totalExpired = expiredCountTotal.rows[0].total;
|
||||
const totalDayExpired = expiredCountDay.rows[0].total;
|
||||
const expiredContracts = expiredResult.rows;
|
||||
|
||||
if(totalExpired >= 1){
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: 'soporte@horuxfin.com',
|
||||
subject: 'Contratos expirados el día de hoy',
|
||||
html: `
|
||||
<h2>Notificación contratos expirados el día de hoy</h2>
|
||||
<p>Cantidad de contratos <b>${totalDayExpired}</b> que expiraron hoy.</p>
|
||||
<ul>Los contratos expirados son</ul>
|
||||
<ul>
|
||||
${expiredContracts.map(c => {
|
||||
const fecha = new Date(c.end_contract);
|
||||
const fechaFormateada = fecha.toLocaleDateString('es-MX');
|
||||
return `<li>
|
||||
ID:${c.id_contract} - RFC: ${c.rfc_employee}
|
||||
- Nombre: ${c.name_employee} -
|
||||
fecha de expiración: ${fechaFormateada}
|
||||
</li>`;
|
||||
}).join('')}
|
||||
</ul>
|
||||
<p>Por favor considere si seran recontratados o no para que se inicie el proceso.</p>`
|
||||
};
|
||||
await transporter.sendMail(mailOptions);
|
||||
res.json({
|
||||
message: 'Correo enviado con contratos expirados hoy',
|
||||
data:totalDayExpired
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: 'Hoy no expiro ningun contrato',
|
||||
data:totalDayExpired
|
||||
});
|
||||
}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo mandar el correo con los contratos por expirar' });
|
||||
}
|
||||
};
|
||||
const expiredContractsMonth = async (req, res) => {
|
||||
try{
|
||||
// Verifica si hoy es el último día del mes
|
||||
if (isLastDayOfMonth()) {
|
||||
return res.status(200).json({
|
||||
message: 'El proceso solo se ejecuta el último día del mes',
|
||||
executed: false
|
||||
});
|
||||
}
|
||||
const endpointName = 'expiredContractsMonth';
|
||||
const current = new Date();
|
||||
const mes = current.getMonth() + 1;
|
||||
const anio = current.getFullYear();
|
||||
const validateEndpoint = await pool.query('SELECT validateendpoint($1,$2::int,$3::int) AS total',[endpointName,mes,anio]);
|
||||
if (validateEndpoint.rows[0].total > 0) {
|
||||
return res.status(400).json({
|
||||
message: `Este endpoint ya fue ejecutado en ${current.toLocaleString('es-MX', { month: 'long' })}.`
|
||||
});
|
||||
}else
|
||||
{
|
||||
await pool.query(`INSERT INTO endpoint_logs (endpoint_name) VALUES ($1)`,[endpointName]);
|
||||
const expiredCountTotal = await pool.query('SELECT COUNT(*) AS total FROM getexpirecontractsmonth()');
|
||||
const expiredResult = await pool.query('SELECT * FROM getexpirecontractsmonth()');
|
||||
const userEmails = await pool.query('SELECT * FROM getusersmailneartoend()');
|
||||
|
||||
const totalExpired = expiredCountTotal.rows[0].total;
|
||||
const expiredContracts = expiredResult.rows;
|
||||
const currentMonth = new Date().toLocaleString('es-MX', { month: 'long' });
|
||||
const users = userEmails.rows;
|
||||
|
||||
if(totalExpired >= 1 && users.length > 0){
|
||||
const recipients = users.map(u => u.email);
|
||||
const mailOptions = {
|
||||
from: 'soporte@horuxfin.com',
|
||||
to: recipients,
|
||||
subject: `Contratos expirados en el mes ${currentMonth} `,
|
||||
html: `
|
||||
<div style="max-width:600px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;
|
||||
background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;
|
||||
overflow:hidden;">
|
||||
<!-- Header -->
|
||||
<div style="background-color:#7a0c1e;padding:18px 22px;">
|
||||
<h2 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">
|
||||
Notificación contratos expirados del mes de ${currentMonth}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="padding:22px;color:#333333;font-size:14px;line-height:1.6;">
|
||||
<p style="margin-top:0;">
|
||||
En este mes expiraron <b>${totalExpired}.
|
||||
</p>
|
||||
<p style="margin-top:0;">
|
||||
Los contratos expirados son en el mes fueron:
|
||||
</p>
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border:1px solid #ddd;">
|
||||
<thead>
|
||||
<tr style="background-color:#f4f4f4;">
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">ID contract</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">RFC</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Nombre</th>
|
||||
<th align="left" style="padding:8px;border:1px solid #ddd;">Fecha que expiro:</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${expiredContracts.map(c => {
|
||||
const fin = formatDateDMY(c.end_contract);
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.id_contract}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.rfc_employee}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${c.name_employee}</td>
|
||||
<td style="padding:8px;border:1px solid #ddd;">${fin}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- CTA -->
|
||||
<div style="text-align:center;margin-top:32px;">
|
||||
<a href="https://hacienda.consultoria-as.com/"
|
||||
style="background-color:#7a0c1e;color:#ffffff;
|
||||
text-decoration:none;font-weight:bold;
|
||||
padding:14px 28px;border-radius:6px;
|
||||
display:inline-block;">
|
||||
Revisar en la web
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:16px;font-size:12px;color:#777777;text-align:center;">
|
||||
Le recomendamos generar el contrato en caso de recontratación.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="background-color:#f6f6f6;padding:12px 20px;
|
||||
font-size:12px;color:#888888;text-align:center;">
|
||||
Sistema de gestión financiera · Consultoría AS
|
||||
</div>
|
||||
|
||||
</div>`
|
||||
};
|
||||
await transporter.sendMail(mailOptions);
|
||||
res.json({
|
||||
message: 'Correo enviado con contratos expirados en el mes',
|
||||
data:totalExpired
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: 'Este mes no expiro ningun contrato',
|
||||
data:totalExpired
|
||||
});
|
||||
}}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ status: 500, message: 'No se pudo mandar el correo con los contratos por expirar' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const isLastDayOfMonth = () => {
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(today.getDate() + 1);
|
||||
return tomorrow.getDate() === 1;
|
||||
};
|
||||
const formatDateDMY = (value) => {
|
||||
if (!value) return '';
|
||||
const date = value instanceof Date ? value : new Date(value);
|
||||
if (isNaN(date.getTime())) return '';
|
||||
return date.toLocaleDateString('es-MX', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {contractsNearToEnd,paymentDeadLine,expensesspecial,
|
||||
emailbirthday,expiredcontracts,expiredContractsMonth,expensesNearToDeadline};
|
||||
174
backend/hotel_hacienda/src/controllers/payment.controller.js
Normal file
174
backend/hotel_hacienda/src/controllers/payment.controller.js
Normal file
@@ -0,0 +1,174 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const newMonthlyExpense = async (req, res) => {
|
||||
try{
|
||||
const {
|
||||
descriptionex,
|
||||
recurrence_id,
|
||||
payment_type,
|
||||
currency_id,
|
||||
suppliers_id,
|
||||
area,
|
||||
expense_category,
|
||||
day_expense,
|
||||
tax_id,
|
||||
new_subtotal,
|
||||
} = req.body;
|
||||
const subtotal = new_subtotal && !isNaN(parseFloat(new_subtotal))? parseFloat(new_subtotal): 0;
|
||||
const result = await pool.query('SELECT newmonthlyexpensev2($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) AS status',[
|
||||
descriptionex,recurrence_id,payment_type,currency_id,suppliers_id,
|
||||
area,expense_category,day_expense,tax_id,parseFloat(subtotal)]);
|
||||
|
||||
const newMonthly = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Gasto mensual agregado correctamente",
|
||||
status: newMonthly
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se puede agregar el gasto mensual',error });
|
||||
}
|
||||
};
|
||||
const setPaymentStatusMonthly = async (req, res) => {
|
||||
try{
|
||||
|
||||
const {id} = req.params;
|
||||
const {
|
||||
status_payment,
|
||||
tax_id,
|
||||
subtotal,
|
||||
} = req.body;
|
||||
const total = subtotal && subtotal !== '' ? subtotal : null;
|
||||
if(total ==null)
|
||||
{
|
||||
const result = await pool.query('SELECT setpaymentstatusmonthlyv2($1,$2) as status',[id,status_payment]);
|
||||
const statuspayment = result.rows[0].status;
|
||||
res.json({
|
||||
message:'Se actualizo el estatus del pago correctamente',
|
||||
status_monthly: statuspayment
|
||||
});
|
||||
}else{
|
||||
const result = await pool.query('SELECT setpaymentstatusmonthlyv2($1,$2,$3,$4) as status',[id,status_payment,tax_id,parseFloat(total)]);
|
||||
const statuspayment = result.rows[0].status;
|
||||
res.json({
|
||||
message:'Se actualizo estatus y pago correctamente',
|
||||
status_monthly: statuspayment
|
||||
});
|
||||
|
||||
}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo actualizar el pago' });
|
||||
}
|
||||
};
|
||||
|
||||
const refreshMonthlyExpenses = async(req,res) =>{
|
||||
try{
|
||||
const result = await pool.query('SELECT refresh_monthly_expenses() as status');
|
||||
const refresh = result.rows[0].status;
|
||||
res.json({
|
||||
message:"se realizo actualizacion de gastos mensuales",
|
||||
status_monthly: refresh
|
||||
});
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo actualizar' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const updateMonthlyExpense = async (req, res) => {
|
||||
try{
|
||||
const {
|
||||
descriptionex,
|
||||
recurrence_id,
|
||||
payment_type,
|
||||
currency_id,
|
||||
suppliers_id,
|
||||
area,
|
||||
expense_category,
|
||||
day_expense,
|
||||
tax_id,
|
||||
new_subtotal,
|
||||
} = req.body;
|
||||
const {id} = req.params;
|
||||
const subtotal = new_subtotal && !isNaN(parseFloat(new_subtotal))? parseFloat(new_subtotal): 0;
|
||||
const result = await pool.query('SELECT updatemonthlyexpensev2($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) AS status',
|
||||
[id,descriptionex,recurrence_id,payment_type,currency_id,suppliers_id,
|
||||
area,expense_category,day_expense,tax_id,parseFloat(subtotal)]);
|
||||
|
||||
const update = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Gasto mensual actualizado correctamente",
|
||||
status: update
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se puede actualizar el gasto mensual' });
|
||||
}
|
||||
};
|
||||
const getOneMonthly = async (req, res) => {
|
||||
try{
|
||||
const {id} = req.params;
|
||||
const result = await pool.query('SELECT * FROM getONEmonthlypayment($1)',[id]);
|
||||
|
||||
const monthlypayment = result.rows.map(mp => ({
|
||||
expense_id: mp.expense_id,
|
||||
descriptionex: mp.descriptionex,
|
||||
recurrence_id: mp.recurrence_id,
|
||||
payment_type: mp.payment_type,
|
||||
currency_idea: mp.currency_id,
|
||||
suppliers_id: mp.suppliers_id,
|
||||
area: mp.area,
|
||||
expense_category: mp.expense_category,
|
||||
day_expense: mp.day_expense,
|
||||
tax_id: mp.tax_id,
|
||||
new_subtotal: mp.new_subtotal
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:monthlypayment
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el pago mensual' });
|
||||
}
|
||||
|
||||
};
|
||||
const needtorefresh = async (req, res) => {
|
||||
try{
|
||||
const {id} = req.params;
|
||||
const {notrefresh} = req.body;
|
||||
let mensaje;
|
||||
const result = await pool.query('SELECT needToRefresh($1,$2)',[id,notrefresh]);
|
||||
const rows = result.rowCount;
|
||||
if (notrefresh == true)
|
||||
mensaje = 'El gasto mensual se estara actualizando.';
|
||||
else
|
||||
mensaje = 'El gasto mensual NO se estara actualizando'
|
||||
res.json({
|
||||
data:rows,
|
||||
message: mensaje
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo completar la accion' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = {newMonthlyExpense,setPaymentStatusMonthly,refreshMonthlyExpenses,updateMonthlyExpense,getOneMonthly,needtorefresh};
|
||||
513
backend/hotel_hacienda/src/controllers/product.controller.js
Normal file
513
backend/hotel_hacienda/src/controllers/product.controller.js
Normal file
@@ -0,0 +1,513 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const getproductsdisplay = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM getproductsdisplay()'
|
||||
);
|
||||
const productsDisplay = result.rows.map(pro => ({
|
||||
id_product:pro.id_product,
|
||||
image_product: pro.image_product,
|
||||
name_product: pro.name_product,
|
||||
price_product: pro.price_product,
|
||||
units: pro.units
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:productsDisplay
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los productos' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getproduct = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getoneproduct($1)',[parseInt(id)]);
|
||||
const product = result.rows.map(pro => ({
|
||||
id_product: pro.id_product,
|
||||
image_product: pro.image_product,
|
||||
name_product: pro.name_product,
|
||||
sku_product: pro.sku_product,
|
||||
supplier_product: pro.supplier_product,
|
||||
price_product: pro.price_unit,
|
||||
id_tax: pro.tax_product,
|
||||
id_category_pro: pro.category_product,
|
||||
product_type: pro.product_type,
|
||||
stock: pro.stock,
|
||||
id_curreny: pro.currency,
|
||||
id_unit: pro.unit
|
||||
}));
|
||||
|
||||
res.json({
|
||||
data:product
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el producto' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getcategoryproducts = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM getcategoryproducts()'
|
||||
);
|
||||
const productCategory = result.rows.map(cat => ({
|
||||
id_prod_category: cat.id_prod_category,
|
||||
name_prod_category: cat.name_prod_category,
|
||||
spanish_name: cat.spanish_name
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:productCategory
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener las categorias del producto' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const gettypeproduct = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM gettypeproduct()'
|
||||
);
|
||||
const productType = result.rows.map(type => ({
|
||||
id_product_type: type.id_product_type,
|
||||
name_product_type: type.name_product_type,
|
||||
spanish_name: type.spanish_name
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:productType
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los tipos del producto' });
|
||||
}
|
||||
|
||||
};
|
||||
const getsuppliers = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM getsuppliers()'
|
||||
);
|
||||
const suppliers = result.rows.map(supp => ({
|
||||
name_suppliers: supp.name_suppliers,
|
||||
rfc_suppliers: supp.rfc_suppliers,
|
||||
}));
|
||||
|
||||
|
||||
|
||||
res.json({
|
||||
data:suppliers
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los proveedores del producto' });
|
||||
}
|
||||
|
||||
};
|
||||
const newsupplier = async (req, res) => {
|
||||
try{
|
||||
const {new_name_supp,
|
||||
new_rfc_supp,
|
||||
new_mail_supp,
|
||||
new_phone_supp} = req.body
|
||||
const result = await pool.query('SELECT newsupplier($1,$2,$3,$4) AS status',
|
||||
[new_name_supp,new_rfc_supp, new_mail_supp, new_phone_supp]);
|
||||
const newsupplier = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Proveedor agregado correctamente",
|
||||
new_supplier: newsupplier
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al ingresar un nuevo proveedor' });
|
||||
}
|
||||
};
|
||||
|
||||
const updateSupplier = async (req, res) => {
|
||||
try{
|
||||
const {id} = req.params;
|
||||
const {
|
||||
name_supp,
|
||||
rfc_supp,
|
||||
mail_supp ,
|
||||
phone_supp} = req.body
|
||||
const result = await pool.query('SELECT updateSupplier($1,$2,$3,$4) AS status',
|
||||
[parseInt(id),name_supp,rfc_supp, mail_supp, phone_supp]);
|
||||
const updatesupplier = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Proveedor actualizado",
|
||||
new_supplier: updatesupplier
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al actualizar el proveedor' });
|
||||
}
|
||||
};
|
||||
|
||||
const disabledSupplier = async (req, res) => {
|
||||
try{
|
||||
const {supplier_id} = req.body
|
||||
const result = await pool.query('SELECT disableSuppliers($1) AS status', [supplier_id]);
|
||||
const disabled = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Proveedor deshabilitado",
|
||||
new_supplier: disabled
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al deshabilitar' });
|
||||
}
|
||||
};
|
||||
const newproduct = async (req, res) => {
|
||||
try{
|
||||
const {new_name_product,
|
||||
new_sku_product,
|
||||
product_type,
|
||||
new_category,
|
||||
suppliers_id,
|
||||
unit,
|
||||
new_stock,
|
||||
newprice_product,
|
||||
new_tax,
|
||||
currency,
|
||||
image_product} = req.body
|
||||
const result = await pool.query('SELECT newproduct($1,$2,$3::jsonb,$4,$5,$6,$7,$8,$9,$10,$11) AS status',
|
||||
[new_name_product,new_sku_product,JSON.stringify(product_type),new_category,suppliers_id,unit,new_stock,
|
||||
newprice_product,new_tax,currency,image_product]);
|
||||
const newproduct = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Producto agregado correctamente",
|
||||
new_product: newproduct
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al ingresar un nuevo proveedor' });
|
||||
}
|
||||
};
|
||||
const updateproduct = async (req, res) => {
|
||||
try{
|
||||
const { id } = req.params;
|
||||
const {
|
||||
product_type,
|
||||
up_name_product,
|
||||
up_sku_product,
|
||||
suppliers_id,
|
||||
up_price_product,
|
||||
up_id_tax,
|
||||
id_category,
|
||||
new_stock,
|
||||
img_product,
|
||||
currency,
|
||||
unit
|
||||
} = req.body
|
||||
|
||||
const result = await pool.query('SELECT updateproduct($1,$2::jsonb,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12) AS status',
|
||||
[parseInt(id),JSON.stringify(product_type),up_name_product,up_sku_product,suppliers_id,
|
||||
up_price_product,parseInt(up_id_tax),parseInt(id_category),parseInt(new_stock),img_product,currency,unit]);
|
||||
|
||||
const updateProduct = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Se actualizo el producto correctamente",
|
||||
new_product: updateProduct
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo actualizar el usuario'});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
const discardProduct = async (req, res) => {
|
||||
try{
|
||||
const { id } = req.params;
|
||||
const { quantity,
|
||||
reasons} = req.body
|
||||
|
||||
const result = await pool.query('SELECT discardproductsstock($1,$2,$3) AS status',
|
||||
[parseInt(id),quantity,reasons]);
|
||||
|
||||
const discardProduct = result.rows[0].status;
|
||||
res.json({
|
||||
message: "Producto descartado correctamente",
|
||||
discard_product: discardProduct
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo hacer el descarte del producto'});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
const getdiscardproduct = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getdiscardproduct()');
|
||||
|
||||
const discartProducts = result.rows.map(dp => ({
|
||||
id_discard: dp.id_discard,
|
||||
product: dp.product,
|
||||
original: dp.original,
|
||||
quantity: dp.quantity,
|
||||
reasons: dp.reasons,
|
||||
date_discard : dp.date_dis,
|
||||
total: dp.total
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvieron los productos descartados",
|
||||
product: discartProducts
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo hacer el descarte del producto'});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
const reportInventory = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM report_inventoryv2()');
|
||||
|
||||
const reportInventory = result.rows.map(ri => ({
|
||||
date_movement: ri.date_movement,
|
||||
id_product: ri.id_product,
|
||||
name_product: ri.name_product,
|
||||
category_en: ri.category_en,
|
||||
category_esp:ri.category_esp,
|
||||
movement: ri.movement,
|
||||
comments_discard: ri.comments_discard,
|
||||
housekeeper_name: ri.housekepper_name,
|
||||
quantity: ri.quantity,
|
||||
average_cost: ri.average_cost,
|
||||
before_stock: ri.before_stock,
|
||||
stock : ri.stock,
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvo el reporte de inventario",
|
||||
new_product: reportInventory
|
||||
});
|
||||
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo obtener el reporte'});
|
||||
}
|
||||
};
|
||||
const getstockadjusments = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getstockadjusments()');
|
||||
|
||||
const productAdjusment = result.rows.map(ri => ({
|
||||
id_product: ri.id_product,
|
||||
name_product: ri.name_product,
|
||||
category_en: ri.category_en,
|
||||
category_esp: ri.category_esp,
|
||||
stock: ri.stock,
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvo el reporte de inventario",
|
||||
productAdjusment: productAdjusment
|
||||
});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo obtener el reporte'});
|
||||
}
|
||||
};
|
||||
|
||||
const setstockadjusments = async (req, res) => {
|
||||
try{
|
||||
const { stockproductadjusment} = req.body
|
||||
const result = await pool.query('SELECT * FROM setstockadjusmentsv2($1::jsonb) as stockadjus',[JSON.stringify(stockproductadjusment)]);
|
||||
|
||||
const stockproduct = result.rows[0].stockadjus;
|
||||
res.json({
|
||||
message: "Se hicieron los ajustes correspondientes",
|
||||
stockproduct: stockproduct});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo realizar el ajuste'});
|
||||
}
|
||||
}
|
||||
const getHouseKeeper = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM gethousekeeper()');
|
||||
const houseKeepers = result.rows.map(hk => ({
|
||||
rfc_employee: hk.rfc_employee,
|
||||
name_emp: hk.name_emp,
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvieron los datos",
|
||||
houseKeeper: houseKeepers});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo obtener los datos'});
|
||||
}
|
||||
}
|
||||
const newConsumptionStock = async (req, res) => {
|
||||
try{
|
||||
const {
|
||||
product_id,
|
||||
quantity_consumption,
|
||||
date_consumption,
|
||||
rfc_emp} = req.body
|
||||
const result = await pool.query('SELECT consumptionstock($1,$2,$3,$4) as STATUS',[product_id,quantity_consumption,date_consumption,rfc_emp]);
|
||||
const status = result.rows[0].status;
|
||||
if(status == 1)
|
||||
message = "El consumo del producto fue realizado con exito";
|
||||
else
|
||||
message = "No se pudo hacer el consumo"
|
||||
res.json({
|
||||
message,
|
||||
status: status
|
||||
});
|
||||
}
|
||||
catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo obtener los datos'});
|
||||
}
|
||||
}
|
||||
const getConsumptionStockReport = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getconsumptionreport()');
|
||||
const stockConsumptions = result.rows.map(sc => ({
|
||||
rfc_employee: sc.rfc_employee,
|
||||
name_emp: sc.name_emp,
|
||||
date_consumption:sc.date_consumption,
|
||||
product_id:sc.product_id,
|
||||
product_name:sc.product_name,
|
||||
quantity:sc.quantity,
|
||||
stocktotal:sc.stocktotal
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvieron los datos",
|
||||
stockConsumption: stockConsumptions});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo obtener los datos'});
|
||||
}
|
||||
};
|
||||
const getproducts = async (req, res) => {
|
||||
try{
|
||||
const page = parseInt(req.query.page) || 1; // Página actual
|
||||
const limit = parseInt(req.query.limit) || 10; // Cantidad por página
|
||||
const offset = (page - 1) * limit; // Desde dónde empezar
|
||||
const result = await pool.query('SELECT * FROM getproducts() LIMIT $1 OFFSET $2',[limit, offset]);
|
||||
|
||||
const totalResult = await pool.query('SELECT COUNT(*) FROM products');
|
||||
const total = parseInt(totalResult.rows[0].count);
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
const products = result.rows.map(pro => ({
|
||||
id_product: pro.id_product,
|
||||
name_product:pro.name_product,
|
||||
sku_product:pro.sku_product,
|
||||
price_product:pro.price_product,
|
||||
id_tax:pro.id_tax,
|
||||
percentage: pro.percentage,
|
||||
id_prod_category: pro.id_prod_category,
|
||||
name_prod_category: pro.name_prod_category,
|
||||
id_unit: pro.id_unit,
|
||||
name_unit: pro.name_unit,
|
||||
id_currency: pro.id_currency,
|
||||
name_currency: pro.name_currency,
|
||||
image_product: pro.image_product,
|
||||
id_suppliers: pro.id_suppliers,
|
||||
name_suppliers: pro.name_suppliers
|
||||
}));
|
||||
res.json({
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages,
|
||||
message: "Se obtuvieron los datos",
|
||||
product: products});
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo obtener los datos'});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports =
|
||||
{
|
||||
getproductsdisplay,
|
||||
getproducts,
|
||||
getcategoryproducts,
|
||||
gettypeproduct,
|
||||
getsuppliers,
|
||||
getproduct,
|
||||
newsupplier,
|
||||
newproduct,
|
||||
updateproduct,
|
||||
discardProduct,
|
||||
getdiscardproduct,
|
||||
reportInventory,
|
||||
getstockadjusments,
|
||||
setstockadjusments,
|
||||
getHouseKeeper,
|
||||
newConsumptionStock,
|
||||
getConsumptionStockReport,
|
||||
updateSupplier,
|
||||
disabledSupplier
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
|
||||
const getpurchases = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const result = await pool.query('SELECT * From getpurchases()');
|
||||
const purchases = result.rows.map(re => ({
|
||||
id_purchase_dt: re.id_purchase_dt,
|
||||
id_expense :re.id_expense ,
|
||||
id_product :re.id_product ,
|
||||
product_name: re.product_name,
|
||||
quantity: re.quantity ,
|
||||
delivered: re.delivered ,
|
||||
id_tax :re.id_tax ,
|
||||
total : re.total
|
||||
|
||||
}));
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron todos los Purchases details",
|
||||
data:purchases
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los purchases details.',error });
|
||||
}
|
||||
};
|
||||
const purchaseid = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {id} = req.params;
|
||||
const {checking} = req.body;
|
||||
|
||||
console.log(id,checking);
|
||||
const result = await pool.query('SELECT * FROM purchaseentry($1,$2) as status',[id,checking]);;
|
||||
const newentry = result.rows[0].status;
|
||||
res.json({
|
||||
message: 'Se obtuvo el Purchases details', newentry
|
||||
|
||||
});
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el Purchases details',error });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {getpurchases,purchaseid};
|
||||
@@ -0,0 +1,44 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const getReportContracts = async (req, res) => {
|
||||
try {
|
||||
// Leer query params con valores por defecto
|
||||
const page = parseInt(req.query.page) || 1; // Página actual
|
||||
const limit = parseInt(req.query.limit) || 10; // Cantidad por página
|
||||
const offset = (page - 1) * limit; // Desde dónde empezar
|
||||
|
||||
// Llamamos a la función con LIMIT y OFFSET
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM reportcontracts() LIMIT $1 OFFSET $2`,
|
||||
[limit, offset]
|
||||
);
|
||||
|
||||
// Obtener total para calcular páginas
|
||||
const totalResult = await pool.query('SELECT COUNT(*) FROM contracts');
|
||||
const total = parseInt(totalResult.rows[0].count);
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
const contracts = result.rows.map(con => ({
|
||||
name_employee: con.id_contract,
|
||||
position_employee: con.name_employee,
|
||||
area_employee: con.position_employee,
|
||||
contract_end: con.end_contract,
|
||||
daily_pay: con.daily_pay,
|
||||
status_contracts: con.status_contracts
|
||||
}));
|
||||
|
||||
res.json({
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages,
|
||||
data: contracts
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener los contratos' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { getReportContracts };
|
||||
@@ -0,0 +1,39 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
|
||||
const getSharedExpenses = async (req, res) => {
|
||||
try {
|
||||
|
||||
const result = await pool.query(`SELECT * FROM shared_expenses_dashboard`);
|
||||
|
||||
const data = result.rows.map(row => ({
|
||||
id_expense: row.id_expense,
|
||||
id_properties: row.id_properties,
|
||||
id_expense_type: row.id_expense_type,
|
||||
request_date: row.request_date,
|
||||
payment_deadline: row.payment_deadline,
|
||||
id_area: row.id_area,
|
||||
id_expense_cat: row.id_expense_cat,
|
||||
id_approval_stat: row.id_approval_stat,
|
||||
id_currency: row.id_currency,
|
||||
is_purchase: row.is_purchase,
|
||||
total: row.total,
|
||||
subtotal: row.subtotal,
|
||||
iva: row.iva,
|
||||
ieps: row.ieps,
|
||||
id_suppliers: row.id_suppliers,
|
||||
id_payment_status: row.id_payment_status,
|
||||
id_cat_percent: row.id_cat_percent,
|
||||
participation: row.participation,
|
||||
id_fix_variable: row.id_fix_variable
|
||||
}));
|
||||
|
||||
res.json({ count: data.length, data });
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Error al obtener los datos del dashboard' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { getSharedExpenses };
|
||||
@@ -0,0 +1,118 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const totalrevenue = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body
|
||||
const result = await pool.query('SELECT * FROM totalrevenue_rest($1,$2) as status',[start_date,end_date]);
|
||||
const totalrenueve = result.rows[0].status;
|
||||
/*const totalrenueve = result.rows.map(re => ({
|
||||
total: re.total
|
||||
}));*/
|
||||
|
||||
|
||||
res.json({
|
||||
data:totalrenueve
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
const cogs = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body;
|
||||
const result = await pool.query('SELECT * FROM cogs_rest($1,$2) as status',[start_date,end_date]);
|
||||
const cogs = result.rows[0].status;
|
||||
/*const cogs = result.rows.map(re => ({
|
||||
cogs_total: re.cogs_total,
|
||||
}));*/
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:cogs
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: error });
|
||||
}
|
||||
};
|
||||
|
||||
const grossprofit = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body;
|
||||
const result = await pool.query('SELECT * FROM grossprofit_rest($1,$2) as status',[start_date,end_date]);
|
||||
const grossprofit = result.rows[0].status;
|
||||
/*const grossprofit = result.rows.map(re => ({
|
||||
grossprofit: re.grossprofit
|
||||
}));*/
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:grossprofit
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const weightedCategoriesCost = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body
|
||||
const result = await pool.query('SELECT * FROM weighted_categories_rest_cost($1,$2)',[start_date,end_date]);
|
||||
const weightedCategoriesCost = result.rows.map(re => ({
|
||||
id_expense_cat : re.id_expense_cat ,
|
||||
category_name: re.category_name ,
|
||||
spanish_name : re.spanish_name ,
|
||||
total : re.total,
|
||||
participation : re.participation
|
||||
}));
|
||||
res.json({
|
||||
data:weightedCategoriesCost
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
const ebitda = async (req, res) =>
|
||||
{
|
||||
try{
|
||||
const {start_date,end_date} = req.body;
|
||||
const result = await pool.query('SELECT * FROM ebitda_rest($1,$2)', [start_date,end_date]);
|
||||
const ebitda = result.rows.map(re => ({
|
||||
expenses_total: re.expenses_total,
|
||||
ebitda : re.ebitda
|
||||
}));
|
||||
|
||||
|
||||
res.json({
|
||||
message: "Se obtuvieron los ingresos",
|
||||
data:ebitda
|
||||
});
|
||||
|
||||
}catch(error)
|
||||
{
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener el dato' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {ebitda,weightedCategoriesCost,grossprofit,totalrevenue,cogs};
|
||||
188
backend/hotel_hacienda/src/controllers/settings.controller.js
Normal file
188
backend/hotel_hacienda/src/controllers/settings.controller.js
Normal file
@@ -0,0 +1,188 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const addNewRoom = async (req, res) => {
|
||||
try{
|
||||
const {room,units,cost_per_nigth,guest,bed,amenities,products} = req.body;
|
||||
|
||||
const result = await pool.query(`SELECT new_room($1,$2,$3,$4,$5,$6::jsonb,$7::jsonb) AS room_id`,
|
||||
[room,units,cost_per_nigth,guest,bed,JSON.stringify(amenities),JSON.stringify(products)]);
|
||||
const newRoom = result.rows[0].room_id;
|
||||
res.json({
|
||||
message: "Se agrego correctamente la habitacion",
|
||||
new_room: newRoom
|
||||
});
|
||||
}catch(error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo agregar nueva habitacion' });
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
const reportRoom = async(req,res) =>{
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM reportrooms()');
|
||||
|
||||
const rooms = result.rows.map(ro => ({
|
||||
id_room: ro.id_room,
|
||||
name_room: ro.name_room,
|
||||
guests: ro.guests,
|
||||
bed_type: ro.bed_type,
|
||||
quantity: ro.quantity
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvo el reporte de habitaciones",
|
||||
new_product: rooms
|
||||
});
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No pudo recuperar la tabla de reporte.'});
|
||||
}
|
||||
};
|
||||
const addNewProperty = async (req, res) => {
|
||||
try{
|
||||
const {new_name,addres_pro,main_area_pro,rfc_pro,phone,areas,services} = req.body;
|
||||
|
||||
const result = await pool.query(`SELECT new_property($1,$2,$3,$4,$5,$6::jsonb,$7::jsonb) AS property_id`,
|
||||
[new_name,addres_pro,main_area_pro,rfc_pro,phone,JSON.stringify(areas),JSON.stringify(services)]);
|
||||
const newRoom = result.rows[0].property_id;
|
||||
res.json({
|
||||
message: "Se agrego correctamente la propiedad",
|
||||
new_property: newRoom
|
||||
});
|
||||
}catch(error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo agregar nueva propiedad' });
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
const reportProperties = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM reportproperties()');
|
||||
const property = result.rows.map(ro => ({
|
||||
id_prop: ro.id_prop,
|
||||
name_property: ro.name_property,
|
||||
areas: ro.areas
|
||||
}));
|
||||
res.json({
|
||||
message: "Se obtuvo el reporte de propiedades",
|
||||
reportProperties: property
|
||||
});
|
||||
}catch(error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo consultar las propiedades' });
|
||||
}
|
||||
};
|
||||
|
||||
const getunits = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getunits()');
|
||||
const units = result.rows.map(u => ({
|
||||
id_unit: u.id_unit,
|
||||
name_unit: u.name_unit}));
|
||||
res.json({
|
||||
message: "Se obtuvieron las unidades de medida",
|
||||
units: units
|
||||
});
|
||||
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo recuperar las unidades' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getapproveby = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getapproveby()');
|
||||
const approveby = result.rows.map(ap => ({
|
||||
id_user: ap.id_user,
|
||||
user_name: ap.user_name}));
|
||||
res.json({
|
||||
message: "Se obtuvieron las aprobadores",
|
||||
approve: approveby
|
||||
});
|
||||
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo recuperar los aprobadores' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getcurrency = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getcurrency()');
|
||||
const currency = result.rows.map(u => ({
|
||||
id_currency: u.id_currency,
|
||||
name_currency: u.name_currency}));
|
||||
res.json({
|
||||
message: "Se obtuvieron las monedas",
|
||||
currency: currency
|
||||
});
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo recuperar las monedas' });
|
||||
}
|
||||
|
||||
};
|
||||
const getrecurrence = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getrecurrence()');
|
||||
const recurrence = result.rows.map(u => ({
|
||||
id_recurrence: u.id_recurrence,
|
||||
name_recurrence: u.name_recurrence}));
|
||||
res.json({
|
||||
message: "Se obtuvieron las recurrencias",
|
||||
currency: recurrence
|
||||
});
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo recuperar la recurrencia' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getrequestby = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getrequestby()');
|
||||
const units = result.rows.map(u => ({
|
||||
id_user: u.id_user,
|
||||
user_name: u.user_name}));
|
||||
res.json({
|
||||
message: "Se obtuvieron los solicitantes",
|
||||
request: units
|
||||
});
|
||||
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo recuperar los solicitantes' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const getcategoryexpense = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT * FROM getcategoryexpense()');
|
||||
const categoryex = result.rows.map(u => ({
|
||||
id_category: u.id_category,
|
||||
name_category: u.name_category}));
|
||||
res.json({
|
||||
message: "Se obtuvieron las categorias del gasto",
|
||||
categoryex: categoryex
|
||||
});
|
||||
|
||||
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'No se pudo las categorias del gasto' });
|
||||
}
|
||||
|
||||
};
|
||||
module.exports = {addNewRoom,reportRoom,addNewProperty,reportProperties,getunits,
|
||||
getcategoryexpense,getrequestby,getcurrency,getapproveby,getrecurrence};
|
||||
107
backend/hotel_hacienda/src/controllers/status.controller.js
Normal file
107
backend/hotel_hacienda/src/controllers/status.controller.js
Normal file
@@ -0,0 +1,107 @@
|
||||
const pool = require('../db/connection');
|
||||
|
||||
const setapprovalstatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {status, approved_by} = req.body;
|
||||
console.log('params:', id);
|
||||
console.log('body:', status);
|
||||
const result = await pool.query('SELECT setapprovalstatus($1,$2,$3) AS status',[id,status,approved_by]);
|
||||
const updateapproval = result.rows[0].status;
|
||||
if(updateapproval == 3)
|
||||
{
|
||||
res.json({
|
||||
message: "El usuario no tiene permisos para aprobar.",
|
||||
new_product: updateapproval
|
||||
});
|
||||
}else{
|
||||
res.json({
|
||||
message: "Se actualizo correctamente el estatus de aprobación",
|
||||
new_product: updateapproval
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al actualizar el estatus de aprobacion' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const setpaymentstatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {status} = req.body;
|
||||
console.log('params:', id);
|
||||
console.log('body:', status);
|
||||
const result = await pool.query('SELECT setpaymentstatusv2($1,$2) AS status',[id,status]);
|
||||
|
||||
const updatepayment = result.rows[0].status;
|
||||
if(updatepayment == -3){
|
||||
res.json({
|
||||
message: "El gasto no esta aprobado o fue rechazado",
|
||||
paymentstatus: updatepayment
|
||||
});}
|
||||
else {
|
||||
res.json({
|
||||
message: "Se actualizo correctamente el estatus del pago",
|
||||
paymentstatus: updatepayment
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al actualizar el estatus del pago' });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const pendingAppPayments = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT pendingapppayments() AS pendingapp',);
|
||||
const pendingapprov = result.rows[0].pendingapp;
|
||||
|
||||
console.log(result.rows[0].pendingApp);
|
||||
res.json({
|
||||
message: "Pagos pendientes de aprobación",
|
||||
data: pendingapprov
|
||||
});
|
||||
|
||||
}catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener pagos pendientes por aprobar' });
|
||||
}
|
||||
};
|
||||
|
||||
const countDelayPayments = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT countdelaypayments() AS delaypayments',);
|
||||
const delayPay = result.rows[0].delaypayments;
|
||||
res.json({
|
||||
message: "Pagos con retraso",
|
||||
data: delayPay
|
||||
});
|
||||
|
||||
}catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener pagos con retraso' });
|
||||
}
|
||||
};
|
||||
const totalSpent = async (req, res) => {
|
||||
try{
|
||||
const result = await pool.query('SELECT totalspent() AS spent',);
|
||||
const totalSpent = result.rows[0].spent;
|
||||
res.json({
|
||||
message: "Total gastado",
|
||||
data: totalSpent
|
||||
});
|
||||
|
||||
}catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Error al obtener total gastado' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { setapprovalstatus,setpaymentstatus,pendingAppPayments,countDelayPayments,totalSpent };
|
||||
16
backend/hotel_hacienda/src/db/connection.js
Normal file
16
backend/hotel_hacienda/src/db/connection.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const { Pool } = require('pg');
|
||||
require('dotenv').config();
|
||||
|
||||
const pool = new Pool({
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME
|
||||
});
|
||||
|
||||
pool.connect()
|
||||
.then(() => console.log('Conectado a PostgreSQL'))
|
||||
.catch(err => console.error('Error de conexión:', err));
|
||||
|
||||
module.exports = pool;
|
||||
15
backend/hotel_hacienda/src/middlewares/handleValidation.js
Normal file
15
backend/hotel_hacienda/src/middlewares/handleValidation.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const { validationResult } = require('express-validator');
|
||||
|
||||
const handleValidation = (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
status: 400,
|
||||
message: 'Error en la validación de datos',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = handleValidation;
|
||||
12
backend/hotel_hacienda/src/middlewares/validators.js
Normal file
12
backend/hotel_hacienda/src/middlewares/validators.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const { body } = require('express-validator');
|
||||
|
||||
const loginValidator = [
|
||||
body('name_mail_user')
|
||||
.trim()
|
||||
.notEmpty().withMessage('El correo o nombre de usuario es obligatorio'),
|
||||
body('user_pass')
|
||||
.trim()
|
||||
.notEmpty().withMessage('La contraseña es obligatoria')
|
||||
];
|
||||
|
||||
module.exports = { loginValidator };
|
||||
File diff suppressed because it is too large
Load Diff
11
backend/hotel_hacienda/src/routes/auth.routes.js
Normal file
11
backend/hotel_hacienda/src/routes/auth.routes.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const authController = require('../controllers/auth.controller');
|
||||
const { loginValidator } = require('../middlewares/validators');
|
||||
const handleValidation = require('../middlewares/handleValidation');
|
||||
|
||||
router.post('/login',loginValidator, handleValidation, authController.login);
|
||||
router.post('/createuser',authController.createuser);
|
||||
router.post('/recoverpass',authController.passRecover);
|
||||
|
||||
module.exports = router;
|
||||
16
backend/hotel_hacienda/src/routes/contract.routes.js
Normal file
16
backend/hotel_hacienda/src/routes/contract.routes.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const contractController = require('../controllers/contract.controller');
|
||||
|
||||
router.get('/', contractController.getContracts);
|
||||
router.get('/getinfocontract/:id', contractController.getContract);
|
||||
router.get('/neartoend', contractController.neartoend);
|
||||
router.get('/positions', contractController.getpositions);
|
||||
router.get('/areas', contractController.getareas);
|
||||
router.get('/bosses', contractController.getbosses);
|
||||
router.get('/reportempcontract', contractController.reportEmployeeContract);
|
||||
router.get('/disabledcontract', contractController.disabledcontracts);
|
||||
|
||||
router.post('/newcontract',contractController.newContract);
|
||||
router.put('/updatecontract/:id', contractController.updateContract);
|
||||
module.exports = router;
|
||||
14
backend/hotel_hacienda/src/routes/employee.routes.js
Normal file
14
backend/hotel_hacienda/src/routes/employee.routes.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const employeeController = require('../controllers/employee.controller');
|
||||
|
||||
router.get('/', employeeController.getEmployees);
|
||||
router.get('/activeEmployees',employeeController.getTotalActiveEmployees);
|
||||
router.get('/getattendance',employeeController.getattendance);
|
||||
router.get('/gradeofstudy',employeeController.getGradeOfStudy);
|
||||
router.get('/relationship',employeeController.getRelationshipEmployee);
|
||||
router.post('/employee',employeeController.getEmployee);
|
||||
router.post('/newemployee',employeeController.newEmployee);
|
||||
router.post('/updateemployee',employeeController.updateEmployee);
|
||||
|
||||
module.exports = router;
|
||||
8
backend/hotel_hacienda/src/routes/exchange.routes.js
Normal file
8
backend/hotel_hacienda/src/routes/exchange.routes.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const exchangeRouter = require('../controllers/exchange.controller');
|
||||
|
||||
router.post('/consultexchange',exchangeRouter.consultExchange );
|
||||
router.get('/getexchanges',exchangeRouter.getExchange );
|
||||
|
||||
module.exports = router;
|
||||
20
backend/hotel_hacienda/src/routes/expense.routes.js
Normal file
20
backend/hotel_hacienda/src/routes/expense.routes.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const expenseController = require('../controllers/expense.controller');
|
||||
|
||||
|
||||
router.get('/pendingapproval', expenseController.getPendingAppExpenses);
|
||||
router.get('/approvedexpenses', expenseController.getApprovedAppExpenses);
|
||||
router.get('/rejectedexpenses', expenseController.getRejectedappExpenses);
|
||||
router.get('/mainsupplier',expenseController.mainSupplier);
|
||||
router.put('/getexpense/:id',expenseController.getExpense);
|
||||
router.get('/getinfo', expenseController.getInfoExpense);
|
||||
router.get('/reportexpenses', expenseController.getReportExpenses);
|
||||
router.post('/countpending',expenseController.countpending);
|
||||
router.get('/reportpayments',expenseController.getReportPayments);
|
||||
router.get('/monthlypayments',expenseController.getmonthlypayments);
|
||||
router.post('/newexpense',expenseController.newExpense);
|
||||
router.post('/totalapproved', expenseController.getTotalApproved);
|
||||
router.put('/updateexpense/:id',expenseController.updateExpense);
|
||||
router.get('/gettaxes',expenseController.getTaxes);
|
||||
module.exports = router;
|
||||
14
backend/hotel_hacienda/src/routes/hotelpl.routes.js
Normal file
14
backend/hotel_hacienda/src/routes/hotelpl.routes.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const hotelControllers = require('../controllers/hotelp.controller');
|
||||
|
||||
router.post('/cogs', hotelControllers.cogs);
|
||||
router.post('/ebitda', hotelControllers.ebitda);
|
||||
router.post('/employeeshare', hotelControllers.employeeshare);
|
||||
router.post('/grossprofit', hotelControllers.grossprofit);
|
||||
router.post('/tips', hotelControllers.tips);
|
||||
router.post('/totalrevenue', hotelControllers.totalrevenue);
|
||||
router.post('/weightedCategoriesCost', hotelControllers.weightedCategoriesCost);
|
||||
|
||||
|
||||
module.exports = router;
|
||||
25
backend/hotel_hacienda/src/routes/incomehrx.routes.js
Normal file
25
backend/hotel_hacienda/src/routes/incomehrx.routes.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const incomeController = require('../controllers/incomehrx.controller');
|
||||
|
||||
router.get('/accountincome', incomeController.getAccountIncome);
|
||||
router.get('/categoryincome', incomeController.getCategoryIncome);
|
||||
router.get('/invoiceIncome', incomeController.getInvoiceIncome);
|
||||
router.get('/totalIncome', incomeController.getTotalIncome);
|
||||
router.get('/incomehorux', incomeController.getIncomeHorux);
|
||||
router.get('/oneincomehorux/:id', incomeController.getOneIncome);
|
||||
router.get('/stripedata/', incomeController.getstripeservice);
|
||||
router.post('/stripedatademo/', incomeController.addDemostripeData);
|
||||
router.post('/insertinvoice', incomeController.addfacturas);
|
||||
router.post('/newincome', incomeController.newIncome);
|
||||
router.put('/updateincome/:id', incomeController.updateIncome);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
||||
34
backend/hotel_hacienda/src/routes/incomes.routes.js
Normal file
34
backend/hotel_hacienda/src/routes/incomes.routes.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const incomeController = require('../controllers/incomes.controller');
|
||||
|
||||
|
||||
router.post('/getincomes',incomeController.getincomes);
|
||||
router.post('/totalincomes',incomeController.totalincomes);
|
||||
router.post('/channelscards',incomeController.channelscards);
|
||||
router.post('/loadincomes',incomeController.loadincomes);
|
||||
router.post('/loadproductsales',incomeController.loadsoftrestaurant);
|
||||
router.post('/loadchequesdetalle',incomeController.loadChequesDetalle);
|
||||
router.post('/reportincomes',incomeController.reportIncomes);
|
||||
router.post('/countticket',incomeController.countFolios);
|
||||
router.post('/efectivo',incomeController.sumaEfectivo);
|
||||
router.post('/otros',incomeController.sumaOtros);
|
||||
router.post('/propinas',incomeController.sumaPropinas);
|
||||
router.post('/tarjeta',incomeController.sumaTarjeta);
|
||||
router.post('/vales',incomeController.sumaVales);
|
||||
router.post('/sumatotal',incomeController.sumasTotales);
|
||||
router.post('/ticketpromedio',incomeController.ticketPromedio);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
router.get('/getproductsales',incomeController.getSoftRestaurant);
|
||||
router.get('/getdetallecheque',incomeController.getdetallecheques);
|
||||
|
||||
module.exports = router;
|
||||
12
backend/hotel_hacienda/src/routes/mail.routes.js
Normal file
12
backend/hotel_hacienda/src/routes/mail.routes.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const emailController = require('../controllers/mail.controller');
|
||||
|
||||
router.post('/nearexpiring', emailController.contractsNearToEnd);
|
||||
router.post('/paymentdelay', emailController.paymentDeadLine);
|
||||
router.post('/expensesneartodeadline', emailController.expensesNearToDeadline);
|
||||
router.post('/expensesspecial',emailController.expensesspecial);
|
||||
router.post('/birthdays',emailController.emailbirthday);
|
||||
router.post('/contractexpired',emailController.expiredcontracts);
|
||||
router.post('/expiredcontractsmonth',emailController.expiredContractsMonth);
|
||||
module.exports = router;
|
||||
11
backend/hotel_hacienda/src/routes/payment.routes.js
Normal file
11
backend/hotel_hacienda/src/routes/payment.routes.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const paymentController = require('../controllers/payment.controller');
|
||||
|
||||
router.post('/newexpmonthly', paymentController.newMonthlyExpense);
|
||||
router.get('/refreshmonthly',paymentController.refreshMonthlyExpenses);
|
||||
router.put('/paymentstatusmonthly/:id', paymentController.setPaymentStatusMonthly);
|
||||
router.put('/updateexpmonthly/:id',paymentController.updateMonthlyExpense);
|
||||
router.get('/onemothlyexpense/:id', paymentController.getOneMonthly);
|
||||
router.put('/needtorefresh/:id', paymentController.needtorefresh);
|
||||
module.exports = router;
|
||||
27
backend/hotel_hacienda/src/routes/product.routes.js
Normal file
27
backend/hotel_hacienda/src/routes/product.routes.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const productController = require('../controllers/product.controller');
|
||||
const { route } = require('./contract.routes');
|
||||
|
||||
router.get('/', productController.getproductsdisplay);
|
||||
router.get('/productcategory', productController.getcategoryproducts);
|
||||
router.get('/producttype', productController.gettypeproduct);
|
||||
router.get('/suppliers',productController.getsuppliers);
|
||||
router.get('/gdiscardproducts',productController.getdiscardproduct);
|
||||
router.get('/reportinventory',productController.reportInventory);
|
||||
router.get('/stockadjusment',productController.getstockadjusments);
|
||||
router.get('/gethousekeeper',productController.getHouseKeeper);
|
||||
router.get('/getproducts',productController.getproducts);
|
||||
router.get('/getconsumptionstockreport',productController.getConsumptionStockReport);
|
||||
|
||||
router.post('/newsupplier',productController.newsupplier);
|
||||
router.post('/newproduct',productController.newproduct);
|
||||
router.post('/stockadjusmentset',productController.setstockadjusments);
|
||||
router.post('/newconsumptionstock',productController.newConsumptionStock);
|
||||
router.post('/disableSupplier',productController.disabledSupplier);
|
||||
|
||||
router.put('/update_product/:id',productController.updateproduct);
|
||||
router.put('/discardproduct/:id',productController.discardProduct);
|
||||
router.put('/product/:id',productController.getproduct);
|
||||
router.put('updatesupplier/:id',productController.updateSupplier);
|
||||
module.exports = router;
|
||||
10
backend/hotel_hacienda/src/routes/purchase.routes.js
Normal file
10
backend/hotel_hacienda/src/routes/purchase.routes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const purchaseController = require('../controllers/purchase.controller');
|
||||
|
||||
|
||||
|
||||
router.get('/getpurchases',purchaseController.getpurchases);
|
||||
router.put('/entry/:id',purchaseController.purchaseid);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const reportContractController = require('../controllers/reportcontract.controller');
|
||||
|
||||
router.get('/', reportContractController.getReportContracts);
|
||||
|
||||
module.exports = router;
|
||||
12
backend/hotel_hacienda/src/routes/restaurantpl.routes.js
Normal file
12
backend/hotel_hacienda/src/routes/restaurantpl.routes.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const restaurantControllers = require('../controllers/restaurantpl.controller');
|
||||
|
||||
router.post('/cogs', restaurantControllers.cogs);
|
||||
router.post('/ebitda', restaurantControllers.ebitda);
|
||||
router.post('/grossprofit', restaurantControllers.grossprofit);
|
||||
router.post('/totalrevenue', restaurantControllers.totalrevenue);
|
||||
router.post('/weightedCategoriesCost', restaurantControllers.weightedCategoriesCost);
|
||||
|
||||
|
||||
module.exports = router;
|
||||
16
backend/hotel_hacienda/src/routes/settings.routes.js
Normal file
16
backend/hotel_hacienda/src/routes/settings.routes.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const settingsController = require('../controllers/settings.controller');
|
||||
|
||||
|
||||
router.post('/newroom', settingsController.addNewRoom);
|
||||
router.post('/newproperty', settingsController.addNewProperty);
|
||||
router.get('/reportrooms', settingsController.reportRoom);
|
||||
router.get('/reportproperties', settingsController.reportProperties);
|
||||
router.get('/approveby', settingsController.getapproveby);
|
||||
router.get('/requestby', settingsController.getrequestby);
|
||||
router.get('/categoryexpense', settingsController.getcategoryexpense);
|
||||
router.get('/currency', settingsController.getcurrency);
|
||||
router.get('/units', settingsController.getunits);
|
||||
router.get('/recurrence', settingsController.getrecurrence);
|
||||
module.exports = router;
|
||||
12
backend/hotel_hacienda/src/routes/status.routes.js
Normal file
12
backend/hotel_hacienda/src/routes/status.routes.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const statusController = require('../controllers/status.controller');
|
||||
|
||||
|
||||
router.put('/approveupdate/:id',statusController.setapprovalstatus);
|
||||
router.put('/paymentupdate/:id',statusController.setpaymentstatus);
|
||||
router.get('/penapppayments',statusController.pendingAppPayments);
|
||||
router.get('/countdelaypay',statusController.countDelayPayments);
|
||||
router.get('/totalspent',statusController.totalSpent);
|
||||
|
||||
module.exports = router;
|
||||
7
backend/hotel_hacienda/src/server.js
Normal file
7
backend/hotel_hacienda/src/server.js
Normal file
@@ -0,0 +1,7 @@
|
||||
require('dotenv').config();
|
||||
const app = require('./app.js'); // Importamos la app
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Servidor corriendo en http://localhost:${PORT}`);
|
||||
});
|
||||
13
backend/hotel_hacienda/src/services/mailService.js
Normal file
13
backend/hotel_hacienda/src/services/mailService.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.EMAIL_HOST,
|
||||
port: process.env.EMAIL_PORT,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: process.env.EMAIL_USER,
|
||||
pass: process.env.EMAIL_PASS,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = transporter;
|
||||
Reference in New Issue
Block a user