"use strict"; const moment = require("moment"); const generateControllers = require("../../core/controllers"); const eventService = require("./event.service"); const eventReservationService = require("./events_reservations.service"); const eventInscriptionService = require("./events_inscriptions.service"); const mailService = require("./mail.service"); const userService = require("../auth/user.service"); const marketingListService = require("./marketing_list.service"); const { extractParamsFromRequest, handleErrorResponse, handleResultResponse, httpStatus, } = require("../../helpers/controller.helper"); const emailHelper = require("../../helpers/mail.helper"); const path = require("path"); const responseTime = require("response-time"); // Module Name const MODULE_NAME = "[eventReservation.controller]"; const controllerOptions = { MODULE_NAME }; /**** Activa una reserva que está en borrador (Draft), descontando asistentes del aforo total del evento * Devuelve la reserva pasada por parametro publicada (publish) o null en el caso de error */ async function activeReservation(reservation) { if (!reservation) throw new Error("activeReservation: reservation should be an object"); //En el caso de ya estar publicada no hacemos nada se devuelve tal cual if (reservation.state === "publish") return reservation; await eventService._updateAforoOfEventReservation(reservation, reservation.assistants); //REvisar confirmados en lista de espera //Finalmente publicamos la reserva solo si no está asociada a la lista de espera if (!reservation.overflow_reservationId) { if (!(await eventReservationService._updatePublishReservation(reservation.id))) { console.log("No se ha podido publicar la reserva del evento"); return null; } reservation.state = "publish"; //Finalmente hay que validar la inscripción del tutor en el caso de aquellos centros que no son aliados if ( (await eventInscriptionService._validateInscriptionTutorOfReservation(reservation.id, reservation.userId)) <= 0 ) { //En el caso de ser un centro aliado no se puede validar la inscripcion ya que se hace a posteriori por lo tanto no se hace nada // console.log("No se ha podido validar la inscripción del tutor por ser de un centro aliado se hace a posteriori en este caso"); // return null; } console.log("reservation.userId>>>>>> ", reservation.userId); //Mandamos el código de reserva para que registra a sus alumnos const user = await userService._getUserById(reservation.userId); reservation.contact_email = user.email; reservation.contact_name = user.name; console.log("reservation.userId>>>>>> ", user.email); console.log("reservation.userId>>>>>> ", user.email); try { console.log("envio correo>>>>>>>>>>> ", user); mailService.sendReservationCollegeEmail(reservation); } catch (error) { console.log(error); } return reservation; } } async function activeReservationById(id) { //Buscar reserva con evento y entidad let reservation = await eventReservationService._getReservaByIdWithEntityAndEvent(id); if ((await activeReservation(reservation)) === null) { console.log("Error activeReservationById"); return false; } return true; } const extraControllers = { checkReservationCode: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); const appVersion = req && req.headers && req.headers["accept-version"] ? req.headers["accept-version"] : null; console.log("checkReservationCode - appVersion: ", appVersion); console.log("checkReservationCode - PARAMS ", params); const eventId = params.params.id; const encodedInvitationCode = params.params.encodedInvitationCode; const registrationCode = Buffer.from(req.params.encodedInvitationCode, "base64").toString("ascii"); try { const result = await eventReservationService._getReservaByCode(eventId, registrationCode); console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>RESULT>>>>>>>>>>>>>>>>>>>>>>>>>><<', result); if (appVersion) { if (appVersion == "1.0.0" || appVersion == "1.0.1" || appVersion == "1.0.2") return handleResultResponse(!!result, null, params, res, httpStatus.OK); else return handleResultResponse(result, null, params, res, httpStatus.OK); } else return handleResultResponse(!!result, null, params, res, httpStatus.OK); } catch (error) { return handleErrorResponse(MODULE_NAME, "checkReservationCode", error, res); } }, getReservationsExcel: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); const eventId = params.params.id; const userId = req.user.id; let entityType = null; let entityId = null; // console.log('params.params.typeOrId>>>>++++++++++++++++++++++++++ 1 ', params.params.typeOrId); //typeOrId puede ser college, partner o el id de la entidad de la reserva const typeOrId = params.params.typeOrId; if (typeOrId !== null) { if (typeOrId === "colleges" || params.params.typeOrId === "partners") entityType = typeOrId; else entityId = typeOrId; } // console.log('params.params.typeOrId>>>>++++++++++++++++++++++++++ 2 ', entityId); // console.log('params.params.typeOrId>>>>++++++++++++++++++++++++++ 3 ', entityType); const reservations = await eventReservationService._getReservationsExcel( req.user, eventId, entityId, entityType, function (result, status) { if (result.messenger.code == "S99001") { console.log(result); res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); res.setHeader("Content-Disposition", "attachment; filename=" + result.data.name); res.attachment(result.data.name); res.download(path.resolve(result.data.path), result.data.name); } else { res.status(status).json(result); } } ); }, sendMailReservation: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); const reservationId = params.params.id; const user = req.user; try { const reservation = await eventReservationService._getReservaByIdWithEntityAndEvent(reservationId); if (!reservation) return handleResultResponse("Reserva no encontrada", null, params, res, httpStatus.NOT_FOUND); try { if (reservation.Entity.contact_email) emailHelper.sendReservationCode( mailService.generateHeaderMailReservation(reservation), mailService.generateBodyMailReservation(reservation) ); } catch (error) { // console.log(error); console.log("No se ha podido mandar email con los códigos de invitación"); } return handleResultResponse(null, null, params, res, httpStatus.OK); } catch (error) { return handleResultResponse("Error al buscar la reserva", null, params, res, httpStatus.NOT_FOUND); } }, sendMailReservationsEvent: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); const eventId = params.params.id; const entityId = params.params.entityId; const type = params.params.type; const user = req.user; let reservations = null; let result = ""; try { if (!entityId) reservations = await eventReservationService._getReservasByEventAndType(eventId, type); else reservations = await eventReservationService._getReservasByEventAndEntity(eventId, entityId); if (!reservations) return handleResultResponse("Reservas no encontradas", null, params, res, httpStatus.NOT_FOUND); try { reservations.forEach(function (reservation) { console.log("mando correo: ", reservation.Entity.name); if (reservation.Entity.contact_email && reservation.Entity.contact_email.length !== 0) { console.log("correo: ", reservation.Entity.contact_email); emailHelper.sendReservationCode( mailService.generateHeaderMailReservation(reservation), mailService.generateBodyMailReservation(reservation) ); result = result + "Invitación con código " + reservation.reservation_code + " enviada a " + reservation.Entity.name + " al destinatario " + reservation.Entity.contact_email + "\n"; } else result = result + "Invitación con código " + reservation.reservation_code + " no se ha enviado proque el correo (" + reservation.Entity.contact_email + ") no es válido\n"; }); } catch (error) { // console.log(error); console.log("No se ha podido mandar email con los códigos de invitación"); } return handleResultResponse(result, null, params, res, httpStatus.OK); } catch (error) { return handleResultResponse("Error al buscar las reservas", null, params, res, httpStatus.NOT_FOUND); } }, recuperateReservationByCode: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); let dataInscription = res.locals.dataInscription; if (!dataInscription) return handleResultResponse( "Error recuperateReservationByCode, prepareDataInscription requerida", null, params, res, httpStatus.NOT_FOUND ); console.log(">>>>>>>>>>>>>>>>>>>> recuperateReservationByCode", dataInscription.type); //SI VIENE CODIGO DE RESERVA, RECUPERAMOS LA RESERVA Y EL EVENTO if (dataInscription.reservationCode) { try { dataInscription.reservation = await eventReservationService._getReservaByCode( dataInscription.eventId, dataInscription.reservationCode ); if (dataInscription.reservation) { dataInscription.reservation = await dataInscription.reservation.toJSON(); dataInscription.event = dataInscription.reservation.Event; } else { // No se ha encontrado return handleResultResponse("Código de reserva no encontrado", null, params, res, httpStatus.NOT_FOUND); } res.locals.dataInscription = dataInscription; } catch (error) { return handleErrorResponse(MODULE_NAME, "recuperateReservationByCode", error, res); } } next(); }, createReservationToEntity: async (req, res, next) => { console.log(">>>>>>>>>>>>>>>>>>>> createReservationToEntity>>>> ",res.locals.dataUser); const params = extractParamsFromRequest(req, res, {}); let dataInscription = res.locals.dataInscription; if (!dataInscription || !dataInscription.event) return handleResultResponse( "Error createReservationToEntity, prepareDataInscription, requerida", null, params, res, httpStatus.NOT_FOUND ); let dataUser = res.locals.dataUser; if (!dataUser) return handleResultResponse( "Error createReservationToEntity, prepareDataInscription, getOrCreateUser requerida", null, params, res, httpStatus.NOT_FOUND ); console.log(">>>>>>>>>>>>>>>>>>>> createReservationToEntity>>>>>>", dataInscription.type); //Si viene group_size crearemos un código de reserva if (dataInscription.groupSize > 1) { if (!dataUser.entityId) { return handleResultResponse( "Error No es posible crear reserva grupal si no pertences a una entidad educativa", null, params, res, httpStatus.NOT_FOUND ); } const reservationData = { reservation_code: eventReservationService._generateReservatioCode(dataInscription.event, dataUser.entityName), state: "draft", //sin confirmar, publish es cuando se descuenta del aforo del evento color: "orange", description: dataInscription.type === "online group" ? "Incripción online en grupo" : "Reserva", init_available_date: dataInscription.event.init_available_date, end_available_date: dataInscription.event.end_available_date, entityId: dataUser.entityId, eventId: dataInscription.event.id, gmt: dataInscription.event.gmt, assistants: dataInscription.groupSize, virtual: dataInscription.type === "online" || dataInscription.type === "online group", userId: dataUser.id, allow_overflow: false, }; //Comprobamos aforo si no es online, si no es posible apuntarse iria a lista de espera if (dataInscription.type !== "online" && dataInscription.type !== "online group") { const plazasDisponibles = dataInscription.event.assistants - dataInscription.event.confirmed; if (plazasDisponibles < reservationData.assistants) if (dataInscription.event.allow_overflow) { reservationData.overflowEventId = dataInscription.event.overflow_eventId; reservationData.description = reservationData.description + " en lista de espera"; console.log("Asigno lista de espera>>>>>>>>>>>>>>>>>>>>>>>", reservationData.eventId); } else return handleResultResponse( "Aforo lleno no es posible efectuar la reserva", null, params, res, httpStatus.NOT_FOUND ); } ///Aqui podríamos validar si ya hay reserva y dar error ya que no pueden meter codigo de reserva y darnos un group_size superior a 1. dataInscription.reservation = await eventReservationService.create( reservationData, generateControllers.buildContext(req, {}) ); dataInscription.reservation = dataInscription.reservation.toJSON(); res.locals.dataInscription = dataInscription; console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>RESERVATION CREADA", dataInscription.reservation); } else console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>no hago nada"); next(); }, activeReservationToEntity: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); let dataInscription = res.locals.dataInscription; let dataUser = res.locals.dataUser; if (!dataUser || !dataInscription || !dataInscription.event) return handleResultResponse( "Error createReservationToEntity, prepareDataInscription, ActiveReservationToEntity requerida", null, params, res, httpStatus.NOT_FOUND ); //En caso de ser online no descontamos ningun aforo ya que no se controla de momento if (dataInscription.type === "online" || dataInscription.type === "online group") { console.log(">> No se aplica descuento de aforo"); next(); return; } //En caso de ser una inscripción normal o una reserva ya publicada no descontamos ningun aforo al evento if (!dataInscription.reservation || dataInscription.reservation.state === "publish") { // || (!dataInscription.reservationCode) || (dataInscription.reservationCode === "")) { console.log(">> No se aplica descuento de aforo ya que es una reserva publicada"); next(); return; } //Si es centro aliado if (dataUser.entityLevel === "aliado") { //Aseguramos que la reserva vaya con su evento dataInscription.reservation.Event = dataInscription.event; const reservationPublicada = await activeReservation(dataInscription.reservation); if (reservationPublicada === null) return handleResultResponse( "Error activeReservationToEntity requerida", null, params, res, httpStatus.NOT_FOUND ); else dataInscription.reservation = reservationPublicada; } next(); }, activarReservation: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); const idResevation = params.params.id; if (!idResevation) return handleResultResponse("Error id de reservation necesario", null, params, res, httpStatus.NOT_FOUND); try { let result = await activeReservationById(idResevation); return handleResultResponse(result, null, params, res, httpStatus.OK); } catch (error) { console.log( "********************************************************************************************", error ); //return handleResultResponse("Error al buscar las reservas", null, params, res, httpStatus.NOT_FOUND); return handleErrorResponse("activarReservation", "methodName", error.stack, res); } }, deleteReservationById: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); const idResevation = params.params.id; if (!idResevation) return handleResultResponse("Error id de reservation necesario", null, params, res, httpStatus.NOT_FOUND); const eventReservation = await eventReservationService._getReservaById(idResevation); if (!eventReservation) return handleResultResponse("Error reserva no existente", null, params, res, httpStatus.NOT_FOUND); try { //Antes de borrar una reserva debemos borrar todas las inscripciones asociadas. eventInscriptionService._deleteInscriptionsByReservation(eventReservation.id); if (eventReservation.state === "publish") { const cantidad = -1 * eventReservation.assistants; eventService._updateAforoOfEventReservation(eventReservation, cantidad); } let result = eventReservationService._deleteReservation(eventReservation.id); return handleResultResponse(result, null, params, res, httpStatus.OK); } catch (error) { return handleResultResponse("Error al eliminar la reserva", null, params, res, httpStatus.NOT_FOUND); } }, /**** Modifica una reserva, si está publicada, actualiza la diferenecia de los asistentes, en el caso de ser modificado, en el aforo del evento (assitants). */ checkAssitantsUpdate: async (req, res, next) => { const params = extractParamsFromRequest(req, res, {}); const idResevation = params.params.id; const NewReservarionAssistants = req.body.assistants; if (!idResevation) return handleResultResponse("Error id de reservation necesario", null, params, res, httpStatus.NOT_FOUND); const eventReservation = await eventReservationService._getReservaById(idResevation); // if (!eventReservation) return handleResultResponse("Error reserva no existente", null, params, res, httpStatus.NOT_FOUND); try { if (eventReservation.state === "publish" && eventReservation.assistants != NewReservarionAssistants) { if (NewReservarionAssistants < eventReservation.confirmed) { return handleResultResponse( "Error el número de asistentes no puede ser menor que el de confirmados", null, params, res, httpStatus.NOT_FOUND ); } const cantidad = NewReservarionAssistants - eventReservation.assistants; await eventService._updateAforoOfEventReservation(eventReservation, cantidad); } next(); } catch (error) { //console.log('checkAssitantsUpdate >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', error.stack); return handleErrorResponse("controllerName", "methodName", error.stack, res); } }, /////////////////////////////////////////////////////////////////// //Esta función se llama desde APP y desde WEB //Inscripción con CODIGO DE RESERVA, SE MODIFICA EL CONFIRMED DE LA RESERVA, YA QUE SE DESCONTARA DEL AFORO DE LA RESERVA // en caso de online, funciona igual ya que descontará la inscripción de la persona del aforo quedando la inscripción online asociada a la reserva /////////////////////////////////////////////////////////////////// createInscriptionReservation: async (req, res, next) => { console.log(">>>>>>>>>>>>>>>>>>>> createInscriptionReservation (event_reservations.controller)"); const params = extractParamsFromRequest(req, res, {}); let dataInscription = res.locals.dataInscription; if (!dataInscription || !dataInscription.event || !dataInscription.reservation) return handleResultResponse( "Error prepareDataInscription, recuperateReservationByCode o createReservationToEntity requerida", null, params, res, httpStatus.NOT_FOUND ); let dataUser = res.locals.dataUser; if (!dataUser) return handleResultResponse("Error getOrCreateUser requerida", null, params, res, httpStatus.NOT_FOUND); try { //CON CODIGO DE RESERVA SE MODIFICA EL CONFIRMED DE LA RESERVA, YA QUE SE DESCONTARA DEL AFORO DE LA RESERVA dataInscription.inscriptionsWithReservationCount = await eventInscriptionService._getCountInscriptionsWithReservation(dataInscription.reservation.id); ++dataInscription.inscriptionsWithReservationCount; /*console.log( "me inscribo por reserva>>>>>>>>>>>>>>>>>>>>>>>>>>><< con asistentes: ", dataInscription ); console.log(dataInscription.reservation.sold_out); console.log(dataInscription.inscriptionsWithReservationCount); */ //COMPROBAMOS SI ES VALIDO O HAY QUE APUNTARLE A LA LISTA DE ESPERA DE LA RESERVA if ( dataInscription.reservation.sold_out == 0 && dataInscription.reservation.assistants >= dataInscription.inscriptionsWithReservationCount ) { dataInscription.validated = false; //Es la inscripción automática del tutor o alguien que tiene el código y eso no puede ser if ( dataInscription.type === "online" || dataInscription.type === "online group" || dataInscription.reservation.state === "publish" ) dataInscription.validated = true; console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", dataInscription.type); //Actualizamos aforo de la lista de espera de la reserva y creamos inscripcion en la lista de espera de la reserva if ( await eventReservationService._updateConfirmedReservation( dataInscription.reservation.id, dataInscription.inscriptionsWithReservationCount ) ) dataInscription.inscription = await eventInscriptionService._createInscription( dataInscription.event.id, dataUser.id, dataInscription.type, dataInscription.validated, dataInscription.source, dataInscription.reservation.id, null ); else return handleResultResponse( "No se ha podido actualizar el aforo de la reserva", null, params, res, httpStatus.NOT_FOUND ); //Ponemos la reserva en SOLD_OUT para que no se pueda apuntar nadie más if (dataInscription.reservation.assistants == dataInscription.inscriptionsWithReservationCount) await eventReservationService._updateSoldOutReservation(dataInscription.reservation.id, true); } // APUNTARSE A LISTA DE ESPERA SI SE PUEDE else { if (dataInscription.reservation.allow_overflow === true) { dataInscription.validated = false; dataInscription.inscriptionsWithReservationCount = await eventInscriptionService._getCountInscriptionsWithReservation( dataInscription.reservation.overflow_reservationId ); ++dataInscription.inscriptionsWithReservationCount; // if (dataInscription.reservation.assistants >= dataInscription.inscriptionsWithReservationCount) { //Actualizamos aforo de la reserva y creamos inscripcion if ( await eventReservationService._updateConfirmedReservation( dataInscription.reservation.overflow_reservationId, dataInscription.inscriptionsWithReservationCount ) ) dataInscription.inscription = await eventInscriptionService._createInscription( dataInscription.event.id, dataUser.userResult.user.id, dataInscription.type, dataInscription.validated, dataInscription.source, dataInscription.reservation.overflow_reservationId, null ); else return handleResultResponse( "No se ha podido actualizar el aforo de la reserva", null, params, res, httpStatus.NOT_FOUND ); } else return handleResultResponse( "Aforo completo de la reserva y no hay lista de espera", null, params, res, httpStatus.NOT_FOUND ); } let Result = await dataInscription.inscription.toJSON(); Result = { reservation: dataInscription.reservation, ...Result, }; // Incluimos correo en sendinblue try { // marketingListService.addMarketingList(dataUser, dataInscription); } catch (error) { console.log("Se ha producido un error al añadir a SenINBlue>>>>>>>>>>>>>>>>><<", error); } //Mandamos correo con entrada o lista de espera try { mailService.sendEmailConfirm(dataUser, dataInscription); } catch (error) { console.log("Se ha producido un error al enviar mail>>>>>>>>>>>>>>>>><<", error); } //MAPEO SALIDA API4 //Tratamos resultado, si hay que remover campos para API4 web if (res.locals.v4){ const arrayFieldsremove = res.locals.v4.removeFields; arrayFieldsremove.forEach(campo => { if (campo in Result) { delete Result[campo]; } }); }; return handleResultResponse(Result, null, params, res, httpStatus.CREATED); } catch (error) { return handleResultResponse("Error al crear la incripción online", null, params, res, httpStatus.NOT_FOUND); } }, }; module.exports = generateControllers(eventReservationService, extraControllers, controllerOptions);