From 5bd146096695dba5b0b88e5cb43703d8922faf4f Mon Sep 17 00:00:00 2001 From: Gowtham M Date: Sat, 15 Nov 2025 11:28:01 +0530 Subject: [PATCH] Email notification --- app/controllers/establishment.controller.js | 8 +- app/controllers/submission.controller.js | 138 +++++++++++++++++++- app/models/establishment_user.model.js | 10 ++ app/models/submission.model.js | 6 + app/models/user.model.js | 4 + app/routes/routes.js | 8 ++ 6 files changed, 171 insertions(+), 3 deletions(-) diff --git a/app/controllers/establishment.controller.js b/app/controllers/establishment.controller.js index 8371aa6..053a05c 100644 --- a/app/controllers/establishment.controller.js +++ b/app/controllers/establishment.controller.js @@ -20,7 +20,7 @@ const path = require("path"); exports.testEmail = async (req, res) => { placeHolderData = { contact_name : 'Gowtham', - portal_url : process.env.BASE_URL, + portal_url : process.env.FE_BASE_URL, username : '--', password : '--', support_email : '--', @@ -309,6 +309,12 @@ exports.getAllEstablishments = async (req, res) => { // Fetch Data const data = await Establishment.findAll({ where: whereClause, + attributes: { + include: [ + [ + Sequelize.literal(`(SELECT COUNT(*) FROM establishment_products ep WHERE ep.establishment_id = establishments.id)`),"product_count" ] + ] + }, include: [ { model: CityTown, as: "establishment_city", attributes: ["name"] }, { model: Emirate, as: "establishment_emirate", attributes: ["name"] }, diff --git a/app/controllers/submission.controller.js b/app/controllers/submission.controller.js index 42bc99b..aeb7a73 100644 --- a/app/controllers/submission.controller.js +++ b/app/controllers/submission.controller.js @@ -11,10 +11,12 @@ const Product = db.Product; const CityTown = db.CityTown; const Emirate = db.Emirate; const QuarterlyWindowsConfiguration = db.QuarterlyWindowsConfiguration; +const User = db.user; const ExcelJS = require("exceljs"); const { Op } = require("sequelize"); const { Sequelize } = require("sequelize"); const { sendEmail } = require("../services/email.service"); +const { sendEmailService } = require("../services/email.service"); const { getQuarterPeriods } = require("../services/quarterService"); @@ -105,10 +107,94 @@ exports.createSubmission = async (req, res) => { } + // const result = await Submission.findByPk(submission.id, { + // include: [{ model: SubmissionProduct, as: "products" },{ model: EstablishmentUser,as: "created_user",attributes: ["name"],}], + // }); + + + // //send email + // const EstablishmentData = await Establishment.findOne({ where: { id :result['establishment_id'] } }); + // const EstablishmentUserData = await EstablishmentUser.findAll({ where: { establishment_id :result['establishment_id'] , is_active : 1} }); + // const AdminUsersData = await User.findAll({ where: { is_active : 1} }); + + // for await (const userObj of EstablishmentUserData) + // { + // placeHolderData = { + // user_name : userObj.name, + // quarter : result['quarter'], + // year : result['year'], + // establishment_name : EstablishmentData['factory_name'], + // submitted_by : result['created_user.name'], + // submission_date : result['created_at'], + // support_email : process.env.SUPPORT_EMAIL, + // support_phone : process.env.SUPPORT_PHONE, + // portal_url : process.env.FE_BASE_URL, + // } + + // await sendEmailService(userObj.email, 'submission_created_mail_to_establishment_user', placeHolderData); + // } + + // for await (const userObj of AdminUsersData) + // { + // placeHolderData = { + // user_name : userObj.name, + // quarter : result['quarter'], + // year : result['year'], + // establishment_name : EstablishmentData['factory_name'], + // submitted_by : result['created_user.name'], + // submission_date : result['created_at'], + // support_email : process.env.SUPPORT_EMAIL, + // support_phone : process.env.SUPPORT_PHONE, + // portal_url : process.env.FE_BASE_URL, + // } + + // await sendEmailService(userObj.email, 'submission_created_mail_to_admin', placeHolderData); + // } + + const result = await Submission.findByPk(submission.id, { - include: [{ model: SubmissionProduct, as: "products" }], + include: [ + { model: SubmissionProduct, as: "products" }, + { model: EstablishmentUser, as: "created_user", attributes: ["name"] }, + ], }); + // Fetch required data for email + const EstablishmentData = await Establishment.findOne({ where: { id: result.establishment_id }}); + const EstablishmentUserData = await EstablishmentUser.findAll({where: { establishment_id: result.establishment_id, is_active: 1 }}); + const AdminUsersData = await User.findAll({where: { is_active: 1 }}); + + // Shared placeholder data + const basePlaceholder = { + quarter: result.quarter, + year: result.year, + establishment_name: EstablishmentData.factory_name, + submitted_by: result.created_user?.name, + submission_date: result.created_at, + support_email: process.env.SUPPORT_EMAIL, + support_phone: process.env.SUPPORT_PHONE, + portal_url: process.env.FE_BASE_URL + }; + + // --- SEND EMAILS TO ESTABLISHMENT USERS --- + const establishmentEmailPromises = EstablishmentUserData.map((userObj) => { + return sendEmailService(userObj.email,"submission_created_mail_to_establishment_user",{...basePlaceholder,user_name: userObj.name,}); + }); + + // --- SEND EMAILS TO ADMINS --- + const adminEmailPromises = AdminUsersData.map((userObj) => { + return sendEmailService(userObj.email,"submission_created_mail_to_admin",{...basePlaceholder,user_name: userObj.name,} ); + }); + + // Execute ALL emails in parallel + await Promise.all([ + ...establishmentEmailPromises, + ...adminEmailPromises, + ]); + + + + res.status(201).json({ status: "success", data: result }); } catch (err) { @@ -634,6 +720,9 @@ exports.approveOrRejectSubmission = async (req, res) => { const { id } = req.params; approve_reject_status = req.body.approve_reject_status; + reject_reason = req.body.reject_reason; + approve_or_reject_by = req.body.approve_or_reject_by; + if(approve_reject_status == 1) { statusString = 'Approved'; @@ -647,7 +736,52 @@ exports.approveOrRejectSubmission = async (req, res) => { return res.status(404).json({ status: "failed", message: "Submission not found" }); // Update submission main data - const result = await submission.update({ approve_reject_status : approve_reject_status , status : statusString }); + const result = await submission.update({ approve_reject_status : approve_reject_status , status : statusString, reject_reason, approve_or_reject_by }); + + + //send email to establishment user + const EstablishmentData = await Establishment.findOne({ where: { id :result['establishment_id'] } }); + const EstablishmentUserData = await EstablishmentUser.findAll({ where: { establishment_id :result['establishment_id'] , is_active : 1} }); + + if(approve_reject_status == 1) + { + for await (const userObj of EstablishmentUserData) + { + placeHolderData = { + user_name : userObj.name, + portal_url : process.env.FE_BASE_URL, + quarter : result['quarter'], + year : result['year'], + establishment_name : EstablishmentData['factory_name'], + submission_date : result['created_at'], + rejection_reason : result['reject_reason'], + support_email : process.env.SUPPORT_EMAIL, + support_phone : process.env.SUPPORT_PHONE, + } + + await sendEmailService(userObj.email, 'submission_approved_mail_to_establishment_user', placeHolderData); + } + + }else{ + + for await (const userObj of EstablishmentUserData) + { + placeHolderData = { + user_name : userObj.name, + portal_url : process.env.FE_BASE_URL, + quarter : result['quarter'], + year : result['year'], + establishment_name : EstablishmentData['factory_name'], + submission_date : result['created_at'], + rejection_reason : result['reject_reason'], + support_email : process.env.SUPPORT_EMAIL, + support_phone : process.env.SUPPORT_PHONE, + } + + await sendEmailService(userObj.email, 'submission_rejected_mail_to_establishment_user', placeHolderData); + } + } + res.status(200).json({ status: "success", diff --git a/app/models/establishment_user.model.js b/app/models/establishment_user.model.js index b847eef..ba78775 100644 --- a/app/models/establishment_user.model.js +++ b/app/models/establishment_user.model.js @@ -66,6 +66,16 @@ module.exports = (sequelize, DataTypes) => { tableName: "establishment_users", } ); + + EstablishmentUser.associate = (models) => { + + EstablishmentUser.belongsTo(models.Establishment, { + foreignKey: 'establishment_id', + as: 'establishments', + }); + + }; + return EstablishmentUser; }; diff --git a/app/models/submission.model.js b/app/models/submission.model.js index fb1428c..c725a1a 100644 --- a/app/models/submission.model.js +++ b/app/models/submission.model.js @@ -32,6 +32,12 @@ module.exports = (sequelize, DataTypes) => { approve_reject_status: { type: DataTypes.INTEGER, defaultValue: 0, + }, + approve_or_reject_by: { + type: DataTypes.INTEGER, + }, + reject_reason: { + type: DataTypes.STRING(255), }, status: { type: DataTypes.STRING(50), diff --git a/app/models/user.model.js b/app/models/user.model.js index 0dd3bf5..1d12781 100644 --- a/app/models/user.model.js +++ b/app/models/user.model.js @@ -30,6 +30,10 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.DATE, allowNull: true, }, + is_active: { + type: DataTypes.BOOLEAN, + defaultValue: true, + }, }); return User; diff --git a/app/routes/routes.js b/app/routes/routes.js index 772fc73..10897a8 100644 --- a/app/routes/routes.js +++ b/app/routes/routes.js @@ -1838,6 +1838,7 @@ router.post("/unit-master/uploadCSV",[ verifySignature, verifyToken, upload.sing * non_emirati_female: { type: integer } * total_emirati: { type: integer } * total_employees: { type: integer } + * created_by: { type: integer } * products: * type: array * items: @@ -1910,6 +1911,7 @@ router.post("/submissions",[verifySignature, verifyToken], submissionController. * non_emirati_female: { type: integer } * total_emirati: { type: integer } * total_employees: { type: integer } + * updated_by: { type: integer } * products: * type: array * items: @@ -2143,6 +2145,12 @@ router.put("/enableOrDisableSubmissionEditAccess/:id",[verifySignature, verifyTo * approve_reject_status: * type: integer * description: 1 for Approve, 2 for Reject + * reject_reason: + * type: string + * description: rejection reason + * approve_or_reject_by: + * type: integer + * description: logged admin user primary id * responses: * 200: * description: Updated successfully