diff --git a/app/controllers/establishment.controller.js b/app/controllers/establishment.controller.js index 7f21b1a..f29411d 100644 --- a/app/controllers/establishment.controller.js +++ b/app/controllers/establishment.controller.js @@ -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; diff --git a/app/controllers/submission.controller.js b/app/controllers/submission.controller.js index 4559502..9ae91c0 100644 --- a/app/controllers/submission.controller.js +++ b/app/controllers/submission.controller.js @@ -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 { diff --git a/app/models/cityTown.model.js b/app/models/cityTown.model.js new file mode 100644 index 0000000..d2bddd0 --- /dev/null +++ b/app/models/cityTown.model.js @@ -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; + }; + \ No newline at end of file diff --git a/app/models/emirate.model.js b/app/models/emirate.model.js new file mode 100644 index 0000000..b30d57b --- /dev/null +++ b/app/models/emirate.model.js @@ -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; + }; + \ No newline at end of file diff --git a/app/models/establishment.model.js b/app/models/establishment.model.js index 689d42f..602da9d 100644 --- a/app/models/establishment.model.js +++ b/app/models/establishment.model.js @@ -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; diff --git a/app/models/establishment_user.model.js b/app/models/establishment_user.model.js index 9cb20d3..dfa0c46 100644 --- a/app/models/establishment_user.model.js +++ b/app/models/establishment_user.model.js @@ -26,7 +26,7 @@ module.exports = (sequelize, DataTypes) => { }, gender: { type: DataTypes.STRING, - allowNull: false, + allowNull: true, }, is_active: { type: DataTypes.BOOLEAN, diff --git a/app/models/index.js b/app/models/index.js index 0089ac5..d6b203e 100644 --- a/app/models/index.js +++ b/app/models/index.js @@ -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, { diff --git a/app/routes/routes.js b/app/routes/routes.js index e1de9c4..2bd6c27 100644 --- a/app/routes/routes.js +++ b/app/routes/routes.js @@ -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;