Establishment changes : GWM

This commit is contained in:
Gowtham M 2025-10-28 22:55:58 +05:30
parent efa18d4f78
commit 291f06f2b2
8 changed files with 552 additions and 165 deletions

View File

@ -3,79 +3,300 @@ const bcrypt = require("bcryptjs");
const Establishment = db.Establishment;
const EstablishmentUser = db.EstablishmentUser;
const EstablishmentPasswordResetRequest = db.EstablishmentPasswordResetRequest;
const CityTown = db.CityTown;
const Emirate = db.Emirate;
const user = db.user;
const { sendEmail } = require("../services/emailHelper"); // custom helper
const logger = require("../services/logger");
// Create Establishment + linked user
exports.createEstablishment = async (req, res) => {
try {
const { establishment_code, factory_name, email, permanent_factory_code, industry_code, license_number, isic_code, emirate, emirati_male,emirati_female,non_emirati_male,non_emirati_female,total_emirati,total_employees, establishment_user, } = req.body;
// if ( !establishment_code || !factory_name || !email || !establishment_user?.name || !establishment_user?.email || !establishment_user?.password )
// {
// res.status(400).send({'status':"failed",'message':"Missing required fields",'data': "" });
// }
// Create Establishment
const establishment = await Establishment.create({
establishment_code,
factory_name,
email,
permanent_factory_code,
industry_code,
license_number,
isic_code,
emirate,
emirati_male,emirati_female,non_emirati_male,non_emirati_female,total_emirati,total_employees,
});
// Hash password for user
// const hashedPassword = await bcrypt.hash(establishment_user.password, 10);
// Create related Establishment User
// const user = await EstablishmentUser.create({
// establishment_id: establishment.id,
// name: establishment_user.name,
// email: establishment_user.email,
// password: hashedPassword,
// });
return res.status(201).send({'status':"success",'message':"Establishment and linked user created successfully",'data': { establishment } });
} catch (err) {
return res.status(500).send({'status':"failed",'message':err.message });
}
};
// Get all establishments (with user details)
exports.getAllEstablishments = async (req, res) => {
try {
const {
establishment_code,
factory_name,
permanent_factory_code,
industry_code,
license_number,
isic_code,
description,
// Establishment Contact Details
establishment_address,
establishment_city_town_id,
establishment_emirate_id,
establishment_postal_code,
establishment_po_box,
establishment_makani_number,
establishment_contact_person_name,
establishment_contact_person_designation,
establishment_mobile_number,
establishment_contact_email,
establishment_website,
// Corporate Details
corporate_same_as_establishment,
corporate_name,
corporate_address,
corporate_city_town_id,
corporate_emirate_id,
corporate_postal_code,
corporate_po_box,
corporate_makani_number,
corporate_contact_person_name,
corporate_contact_person_designation,
corporate_mobile_number,
corporate_email,
corporate_website,
emirati_male,
emirati_female,
non_emirati_male,
non_emirati_female,
total_emirati,
total_employees,
created_by,
establishment_user,
} = req.body;
const data = await Establishment.findAll({
include: [
{
model: EstablishmentUser,
as: "users",
attributes: ["id", "name", "email", "is_active"],
},
],
// Basic Validation
if (!establishment_code || !factory_name || !establishment_user) {
return res.status(400).send({
status: "failed",
message: "Missing required fields: establishment_code, factory_name, email, establishment_user",
});
}
// Check if establishment already exists
const existingEstablishment = await Establishment.findOne({where: { establishment_code }, });
if (existingEstablishment) {
return res.status(400).send({status: "failed",message: "Establishment code already exists", });
}
// Create establishment record
const establishment = await Establishment.create({
establishment_code,
factory_name,
permanent_factory_code,
industry_code,
license_number,
isic_code,
description,
establishment_address,
establishment_city_town_id,
establishment_emirate_id,
establishment_postal_code,
establishment_po_box,
establishment_makani_number,
establishment_contact_person_name,
establishment_contact_person_designation,
establishment_mobile_number,
establishment_contact_email,
establishment_website,
corporate_same_as_establishment,
corporate_name,
corporate_address,
corporate_city_town_id,
corporate_emirate_id,
corporate_postal_code,
corporate_po_box,
corporate_makani_number,
corporate_contact_person_name,
corporate_contact_person_designation,
corporate_mobile_number,
corporate_email,
corporate_website,
emirati_male,
emirati_female,
non_emirati_male,
non_emirati_female,
total_emirati,
total_employees,
created_by,
created_at: new Date(),
});
return res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
// Hash password
const hashedPassword = await bcrypt.hash(establishment_user.password, 10);
} catch (err) {
return res.status(500).send({'status':"failed",'message':err.message });
// Create linked Establishment User
const user = await EstablishmentUser.create({
establishment_id: establishment.id,
name: establishment_user.name,
email: establishment_user.email,
password: hashedPassword,
});
// 🔹 Return success response
return res.status(201).send({
status: "success",
message: "Establishment and linked user created successfully",
data: {
establishment,
user: {
id: user.id,
name: user.name,
email: user.email,
},
},
});
} catch (err) {
return res.status(500).send({status: "failed",message: err.message || "Internal server error",});
}
};
// Get all establishments
// exports.getAllEstablishments = async (req, res) => {
// try {
// const data = await Establishment.findAll({
// include: [
// // {
// // model: EstablishmentUser,
// // as: "users",
// // attributes: ["id", "name", "email", "is_active"],
// // },
// {
// model: CityTown,
// as: "establishment_city",
// attributes: ["name"],
// },
// {
// model: Emirate,
// as: "establishment_emirate",
// attributes: ["name"],
// },{
// model: CityTown,
// as: "corporate_city",
// attributes: ["name"],
// },
// {
// model: Emirate,
// as: "corporate_emirate",
// attributes: ["name"],
// },
// {
// model: user,
// as: "created_user",
// attributes: ["name"],
// }
// ],
// });
// return res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
// } catch (err) {
// return res.status(500).send({'status':"failed",'message':err.message });
// }
// };
exports.getAllEstablishments = async (req, res) => {
try {
let {
page = 1,
limit = 10,
search = "",
emirate_id,
isic_code,
status,
sort_by = "created_at",
sort_order = "DESC",
} = req.query;
page = parseInt(page);
limit = parseInt(limit);
const offset = (page - 1) * limit;
// Build dynamic where clause
const whereClause = {};
// Filters
if (status) whereClause.is_active = status === "active" ? 1 : 0;
if (isic_code) whereClause.isic_code = isic_code;
if (emirate_id) whereClause.establishment_emirate_id = emirate_id;
// Search by name, establishment_code, or contact emails
if (search) {
whereClause[Op.or] = [
{ factory_name: { [Op.like]: `%${search}%` } },
{ establishment_code: { [Op.like]: `%${search}%` } },
{ establishment_contact_email: { [Op.like]: `%${search}%` } },
{ corporate_email: { [Op.like]: `%${search}%` } },
];
}
// Fetch Data
const { count, rows } = await Establishment.findAndCountAll({
where: whereClause,
include: [
{ model: CityTown, as: "establishment_city", attributes: ["name"] },
{ model: Emirate, as: "establishment_emirate", attributes: ["name"] },
{ model: CityTown, as: "corporate_city", attributes: ["name"] },
{ model: Emirate, as: "corporate_emirate", attributes: ["name"] },
{ model: user, as: "created_user", attributes: ["name"] },
],
order: [[sort_by, sort_order]],
limit,
offset,
});
return res.status(200).json({
status: "success",
message: "Fetched establishments successfully",
data: rows,
pagination: {
total_records: count,
current_page: page,
total_pages: Math.ceil(count / limit),
limit,
},
});
} catch (err) {
console.error(err);
return res.status(500).json({
status: "failed",
message: err.message || "Internal server error",
});
}
};
// Get one establishment
exports.getEstablishmentById = async (req, res) => {
try {
const data = await Establishment.findByPk(req.params.id, {
include: [{ model: EstablishmentUser, as: "users" }],
include: [
// {
// model: EstablishmentUser,
// as: "users",
// attributes: ["id", "name", "email", "is_active"],
// },
{
model: CityTown,
as: "establishment_city",
attributes: ["name"],
},
{
model: Emirate,
as: "establishment_emirate",
attributes: ["name"],
},{
model: CityTown,
as: "corporate_city",
attributes: ["name"],
},
{
model: Emirate,
as: "corporate_emirate",
attributes: ["name"],
},
{
model: user,
as: "created_user",
attributes: ["name"],
}
],
});
if (!data) return res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
@ -119,11 +340,42 @@ exports.deleteEstablishment = async (req, res) => {
}
};
exports.getAllEmirates = async (req, res) => {
try {
const emirates = await Emirate.findAll({
attributes: ["id", "name", "code"],
order: [["name", "ASC"]],
});
return res.status(200).send({status: "success", message: "Emirates fetched successfully", data: emirates,});
} catch (err) {
return res.status(500).send({status: "failed",message: err.message || "Internal server error", });
}
};
exports.getAllCityTowns = async (req, res) => {
try {
const { emirate_id } = req.query;
const whereClause = {};
if (emirate_id) {
whereClause.emirate_id = emirate_id;
}
const cities = await CityTown.findAll({
where: whereClause,
attributes: ["id", "name", "emirate_id"],
order: [["name", "ASC"]],
});
return res.status(200).send({status: "success", message: "City/Town fetched successfully", data: cities, });
} catch (err) {
return res.status(500).send({ status: "failed", message: err.message || "Internal server error", });
}
};
// GET ALL RESET REQUESTS
exports.getAllRequests = async (req, res) => {
try {
const requests = await EstablishmentPasswordResetRequest.findAll({
@ -139,7 +391,7 @@ exports.getAllRequests = async (req, res) => {
}
};
// CREATE RESET REQUEST
exports.createRequest = async (req, res) => {
try {
const {
@ -217,42 +469,6 @@ exports.createRequest = async (req, res) => {
};
exports.forgotPasswordRequestOTP = async (req, res) => {
try {
const { establishment_name, establishment_code, registered_email } = req.body;
@ -304,9 +520,6 @@ exports.forgotPasswordRequestOTP = async (req, res) => {
}
};
exports.forgotPasswordVerifyOTP = async (req, res) => {
try {
const { registered_email, otp, password, confirm_password } = req.body;

View File

@ -90,57 +90,7 @@ exports.createSubmission = async (req, res) => {
}
};
// exports.updateSubmission = async (req, res) => {
// try {
// const { id } = req.params;
// const { products = [], ...submissionData } = req.body;
// // Check submission exists
// const submission = await Submission.findByPk(id);
// if (!submission)
// return res.status(404).json({ status: "failed", message: "Submission not found" });
// // Update submission main data
// await submission.update(submissionData);
// // Process products
// for (const product of products) {
// if (product.id) {
// // If product ID exists, update
// const existingProduct = await SubmissionProduct.findOne({
// where: { id: product.id, submission_id: id },
// });
// if (existingProduct) {
// await existingProduct.update(product);
// } else {
// // fallback: if given ID not found, create new
// product.submission_id = id;
// await SubmissionProduct.create(product);
// }
// } else {
// // If product has no ID → create new record
// product.submission_id = id;
// await SubmissionProduct.create(product);
// }
// }
// // Get updated full record
// const updatedSubmission = await Submission.findByPk(id, {
// include: [{ model: SubmissionProduct, as: "products" }],
// });
// res.status(200).json({
// status: "success",
// message: "Submission updated successfully",
// data: updatedSubmission,
// });
// } catch (err) {
// console.error(err);
// res.status(500).json({ status: "failed", message: err.message });
// }
// };
exports.updateSubmission = async (req, res) => {
try {

View File

@ -0,0 +1,21 @@
module.exports = (sequelize, DataTypes) => {
const CityTown = sequelize.define('CityTown', {
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
emirate_id: { type: DataTypes.INTEGER, allowNull: true },
name: { type: DataTypes.STRING, allowNull: false },
code: { type: DataTypes.STRING },
is_active: { type: DataTypes.BOOLEAN, defaultValue: true },
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
}, {
tableName: 'city_towns',
timestamps: false,
});
CityTown.associate = (models) => {
CityTown.belongsTo(models.Emirate, { foreignKey: 'emirate_id', as: 'emirate' });
};
return CityTown;
};

View File

@ -0,0 +1,20 @@
module.exports = (sequelize, DataTypes) => {
const Emirate = sequelize.define('Emirate', {
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false },
code: { type: DataTypes.STRING },
is_active: { type: DataTypes.BOOLEAN, defaultValue: true },
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
}, {
tableName: 'emirates',
timestamps: false,
});
Emirate.associate = (models) => {
Emirate.hasMany(models.CityTown, { foreignKey: 'emirate_id', as: 'cities' });
};
return Emirate;
};

View File

@ -15,10 +15,6 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
},
permanent_factory_code: {
type: DataTypes.STRING,
},
@ -31,17 +27,42 @@ module.exports = (sequelize, DataTypes) => {
isic_code: {
type: DataTypes.STRING,
},
emirate: {
type: DataTypes.STRING,
},
// Establishment Contact Details
establishment_address: { type: DataTypes.STRING },
establishment_city_town_id: { type: DataTypes.INTEGER },
establishment_emirate_id: { type: DataTypes.INTEGER },
establishment_postal_code: { type: DataTypes.STRING },
establishment_po_box: { type: DataTypes.STRING },
establishment_makani_number: { type: DataTypes.STRING },
establishment_contact_person_name: { type: DataTypes.STRING },
establishment_contact_person_designation: { type: DataTypes.STRING },
establishment_mobile_number: { type: DataTypes.STRING },
establishment_contact_email: { type: DataTypes.STRING },
establishment_website: { type: DataTypes.STRING },
// Corporate/Head Office Contact Details
corporate_same_as_establishment: { type: DataTypes.BOOLEAN, defaultValue: false },
corporate_name: { type: DataTypes.STRING },
corporate_address: { type: DataTypes.STRING },
corporate_city_town_id: { type: DataTypes.INTEGER },
corporate_emirate_id: { type: DataTypes.INTEGER },
corporate_postal_code: { type: DataTypes.STRING },
corporate_po_box: { type: DataTypes.STRING },
corporate_makani_number: { type: DataTypes.STRING },
corporate_contact_person_name: { type: DataTypes.STRING },
corporate_contact_person_designation: { type: DataTypes.STRING },
corporate_mobile_number: { type: DataTypes.STRING },
corporate_email: { type: DataTypes.STRING },
corporate_website: { type: DataTypes.STRING },
description: { type: DataTypes.TEXT },
emirati_male: DataTypes.INTEGER,
emirati_female: DataTypes.INTEGER,
non_emirati_male: DataTypes.INTEGER,
non_emirati_female: DataTypes.INTEGER,
total_emirati: DataTypes.INTEGER,
total_employees: {
type: DataTypes.INTEGER,
},
type: DataTypes.INTEGER,},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
@ -71,10 +92,37 @@ module.exports = (sequelize, DataTypes) => {
Establishment.associate = (models) => {
Establishment.hasMany(models.Submission, {
foreignKey: "establishment_id",
as: "establishment"
});
Establishment.belongsTo(models.CityTown, {
foreignKey: 'establishment_city_town_id',
as: 'establishment_city',
});
Establishment.belongsTo(models.Emirate, {
foreignKey: 'establishment_emirate_id',
as: 'establishment_emirate',
});
Establishment.belongsTo(models.CityTown, {
foreignKey: 'corporate_city_town_id',
as: 'corporate_city',
});
Establishment.belongsTo(models.Emirate, {
foreignKey: 'corporate_emirate_id',
as: 'corporate_emirate',
});
Establishment.belongsTo(models.user, {
foreignKey: "created_by",
as: "created_user",
});
};
return Establishment;

View File

@ -26,7 +26,7 @@ module.exports = (sequelize, DataTypes) => {
},
gender: {
type: DataTypes.STRING,
allowNull: false,
allowNull: true,
},
is_active: {
type: DataTypes.BOOLEAN,

View File

@ -26,6 +26,8 @@ db.SubmissionDeadline = require("./submissionDeadline.model")(sequelize, DataTyp
db.NotificationTemplate = require("./notificationTemplate.model")(sequelize, DataTypes);
db.QuarterlyWindowsConfiguration = require("./quarterlyWindowsConfiguration.model")(sequelize, DataTypes);
db.EstablishmentPasswordResetRequest = require("./establishmentPasswordResetRequest.model")(sequelize, DataTypes);
db.Emirate = require("./emirate.model")(sequelize, DataTypes);
db.CityTown = require("./cityTown.model")(sequelize, DataTypes);
// Associations
db.Establishment.hasMany(db.EstablishmentUser, {

View File

@ -51,8 +51,10 @@ const quarterlyWindowsController = require("../controllers/quarterlyWindowsConfi
* description: Manage notification templates
* - name: Quarterly Windows Configuration
* description: Manage quarterly windows configuration master data
* - name: Establishment Password Reset
* - name: Establishment Password Reset Requests
* description: Manage Establishment Password Reset Requests
* - name: Master
* description:
*/
@ -357,18 +359,42 @@ router.delete("/admin_users/:id",[verifySignature, verifyToken], adminUserContro
* properties:
* establishment_code: { type: string }
* factory_name: { type: string }
* email: { type: string }
* permanent_factory_code: { type: string }
* industry_code: { type: string }
* license_number: { type: string }
* isic_code: { type: string }
* emirate: { type: string }
* description: { type: string }
* establishment_address: { type: string }
* establishment_city_town_id: { type: integer, description: "FK from city_towns" }
* establishment_emirate_id: { type: integer, description: "FK from emirates" }
* establishment_postal_code: { type: string }
* establishment_po_box: { type: string }
* establishment_makani_number: { type: string }
* establishment_contact_person_name: { type: string }
* establishment_contact_person_designation: { type: string }
* establishment_mobile_number: { type: string }
* establishment_contact_email: { type: string }
* establishment_website: { type: string }
* corporate_same_as_establishment: { type: boolean }
* corporate_name: { type: string }
* corporate_address: { type: string }
* corporate_city_town_id: { type: integer, description: "FK from city_towns" }
* corporate_emirate_id: { type: integer, description: "FK from emirates" }
* corporate_postal_code: { type: string }
* corporate_po_box: { type: string }
* corporate_makani_number: { type: string }
* corporate_contact_person_name: { type: string }
* corporate_contact_person_designation: { type: string }
* corporate_mobile_number: { type: string }
* corporate_email: { type: string }
* corporate_website: { type: string }
* emirati_male: { type: integer }
* emirati_female: { type: integer }
* non_emirati_male: { type: integer }
* non_emirati_female: { type: integer }
* total_emirati: { type: integer }
* total_employees: { type: integer }
* created_by: { type: integer }
* establishment_user:
* type: object
* required:
@ -393,14 +419,49 @@ router.post("/establishments",[verifySignature, verifyToken], establishmentContr
* @swagger
* /api/establishments:
* get:
* summary: Get all Establishments
* summary: Get all Establishments with pagination, filters, and sorting
* tags: [Establishments]
* security:
* - appSignature: []
* - bearerAuth: []
* parameters:
* - in: query
* name: page
* schema: { type: integer, default: 1 }
* description: Current page number
* - in: query
* name: limit
* schema: { type: integer, default: 10 }
* description: Number of records per page
* - in: query
* name: search
* schema: { type: string }
* description: Search by Establishment name, email, or code
* - in: query
* name: emirate_id
* schema: { type: integer }
* description: Filter by Emirate ID
* - in: query
* name: isic_code
* schema: { type: string }
* description: Filter by ISIC code
* - in: query
* name: status
* schema: { type: string, enum: [active, inactive] }
* description: Filter by active/inactive status
* - in: query
* name: sort_by
* schema: { type: string, default: created_at }
* description: Column to sort by (e.g. name, created_at)
* - in: query
* name: sort_order
* schema: { type: string, enum: [ASC, DESC], default: DESC }
* description: Sorting order
* responses:
* 200:
* description: List of establishments
* description: Fetched establishments successfully
* 500:
* description: Internal server error
*/
router.get("/establishments",[verifySignature, verifyToken], establishmentController.getAllEstablishments);
@ -453,18 +514,42 @@ router.get("/establishments/:id",[verifySignature, verifyToken], establishmentCo
* properties:
* establishment_code: { type: string }
* factory_name: { type: string }
* email: { type: string }
* permanent_factory_code: { type: string }
* industry_code: { type: string }
* license_number: { type: string }
* isic_code: { type: string }
* emirate: { type: string }
* description: { type: string }
* establishment_address: { type: string }
* establishment_city_town_id: { type: integer, description: "FK from city_towns" }
* establishment_emirate_id: { type: integer, description: "FK from emirates" }
* establishment_postal_code: { type: string }
* establishment_po_box: { type: string }
* establishment_makani_number: { type: string }
* establishment_contact_person_name: { type: string }
* establishment_contact_person_designation: { type: string }
* establishment_mobile_number: { type: string }
* establishment_contact_email: { type: string }
* establishment_website: { type: string }
* corporate_same_as_establishment: { type: boolean }
* corporate_name: { type: string }
* corporate_address: { type: string }
* corporate_city_town_id: { type: integer, description: "FK from city_towns" }
* corporate_emirate_id: { type: integer, description: "FK from emirates" }
* corporate_postal_code: { type: string }
* corporate_po_box: { type: string }
* corporate_makani_number: { type: string }
* corporate_contact_person_name: { type: string }
* corporate_contact_person_designation: { type: string }
* corporate_mobile_number: { type: string }
* corporate_email: { type: string }
* corporate_website: { type: string }
* emirati_male: { type: integer }
* emirati_female: { type: integer }
* non_emirati_male: { type: integer }
* non_emirati_female: { type: integer }
* total_emirati: { type: integer }
* total_employees: { type: integer }
* updated_by: { type: integer }
* responses:
* 200:
* description: Establishment updated successfully
@ -2261,6 +2346,54 @@ router.post("/forgot-password/verify-otp", establishmentController.forgotPasswor
/**
* @swagger
* /api/emirates:
* get:
* summary: Get all Emirates
* tags: [Master]
* security:
* - appSignature: []
* - bearerAuth: []
* responses:
* 200:
* description: Emirates fetched successfully
* 500:
* description: Server error
*/
router.get("/emirates", [verifySignature, verifyToken], establishmentController.getAllEmirates);
/**
* @swagger
* /api/city-towns:
* get:
* summary: Get all City/Town (optional filter by emirate_id)
* tags: [Master]
* security:
* - appSignature: []
* - bearerAuth: []
* parameters:
* - in: query
* name: emirate_id
* schema:
* type: integer
* description: Filter cities by emirate_id
* responses:
* 200:
* description: City/Town fetched successfully
* 500:
* description: Server error
*/
router.get("/city-towns", [verifySignature, verifyToken], establishmentController.getAllCityTowns);
module.exports = router;