Added API: getManufacturingMonthlyOverview
This commit is contained in:
parent
454f97e84c
commit
8ce1d8b8e4
@ -1,5 +1,8 @@
|
|||||||
const db = require("../models");
|
const db = require("../models");
|
||||||
const ManufacturingIpi = db.ManufacturingIpi;
|
const ManufacturingIpi = db.ManufacturingIpi;
|
||||||
|
const Isic2DigitIndices = db.Isic2DigitIndices;
|
||||||
|
const Isic3DigitIndices = db.Isic3DigitIndices;
|
||||||
|
const Isic4DigitIndices = db.Isic4DigitIndices;
|
||||||
const logger = require("../services/logger");
|
const logger = require("../services/logger");
|
||||||
|
|
||||||
const formatDecimal = (value) => {
|
const formatDecimal = (value) => {
|
||||||
@ -38,6 +41,153 @@ exports.getAllManufacturingIndexDetails = async (req, res) => {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`getAllManufacturingIndexDetails API: ${error.message}`);
|
logger.error(`getAllManufacturingIndexDetails API: ${error.message}`);
|
||||||
|
logger.error(`Stack trace: ${error.stack}`);
|
||||||
res.status(500).send({ status: "failed", message: "Internal server error" });
|
res.status(500).send({ status: "failed", message: "Internal server error" });
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getManufacturingMonthlyOverviewByYearMonth = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { year, month } = req.query;
|
||||||
|
|
||||||
|
// Validate parameters exist
|
||||||
|
if (!year || !month) {
|
||||||
|
logger.warn('getManufacturingMonthlyOverview API: Missing required parameters');
|
||||||
|
return res.status(400).send({
|
||||||
|
status: "failed",
|
||||||
|
message: "Year and month are required"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize and validate input - prevent SQL injection
|
||||||
|
const yearInt = parseInt(year, 10);
|
||||||
|
const monthInt = parseInt(month, 10);
|
||||||
|
|
||||||
|
// Validate that parsing was successful
|
||||||
|
if (isNaN(yearInt) || isNaN(monthInt)) {
|
||||||
|
logger.warn(`getManufacturingMonthlyOverview API: Invalid input - year: ${year}, month: ${month}`);
|
||||||
|
return res.status(400).send({
|
||||||
|
status: "failed",
|
||||||
|
message: "Year and month must be valid numbers"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate year range (reasonable bounds)
|
||||||
|
if (yearInt < 2000 || yearInt > 2100) {
|
||||||
|
return res.status(400).send({
|
||||||
|
status: "failed",
|
||||||
|
message: "Year must be between 2000 and 2100"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate month range
|
||||||
|
if (monthInt < 1 || monthInt > 12) {
|
||||||
|
return res.status(400).send({
|
||||||
|
status: "failed",
|
||||||
|
message: "Month must be between 1 and 12"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common where clause
|
||||||
|
const whereClause = {
|
||||||
|
year: yearInt,
|
||||||
|
month: monthInt
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch data from all tables in parallel
|
||||||
|
const [manufacturingData, isic2DigitData, isic3DigitData, isic4DigitData] = await Promise.all([
|
||||||
|
ManufacturingIpi.findOne({
|
||||||
|
attributes: ['manufacturing_index', 'mom_change', 'yoy_change'],
|
||||||
|
where: whereClause
|
||||||
|
}),
|
||||||
|
Isic2DigitIndices.findAll({
|
||||||
|
attributes: ['isic_2digit_code', 'isic_description', 'total_weight', 'weighted_index_sum', 'isic_2digit_index'],
|
||||||
|
where: whereClause,
|
||||||
|
order: [['isic_2digit_code', 'ASC']]
|
||||||
|
}),
|
||||||
|
Isic3DigitIndices.findAll({
|
||||||
|
attributes: ['isic_3digit_code', 'isic_description', 'total_weight', 'weighted_index_sum', 'isic_3digit_index'],
|
||||||
|
where: whereClause,
|
||||||
|
order: [['isic_3digit_code', 'ASC']]
|
||||||
|
}),
|
||||||
|
Isic4DigitIndices.findAll({
|
||||||
|
attributes: ['isic_4digit_code', 'isic_description', 'total_weight', 'weighted_index_sum', 'isic_4digit_index'],
|
||||||
|
where: whereClause,
|
||||||
|
order: [['isic_4digit_code', 'ASC']]
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Check if manufacturing data exists (main data)
|
||||||
|
if (!manufacturingData) {
|
||||||
|
logger.info(`getManufacturingMonthlyOverview API: No data found for year ${yearInt}, month ${monthInt}`);
|
||||||
|
return res.status(404).send({
|
||||||
|
status: "failed",
|
||||||
|
message: "No data found for the specified period"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format Manufacturing IPI data
|
||||||
|
const plainManufacturing = manufacturingData.get({ plain: true });
|
||||||
|
const formattedManufacturing = {
|
||||||
|
manufacturing_index: formatDecimal(plainManufacturing.manufacturing_index),
|
||||||
|
mom_change: formatDecimal(plainManufacturing.mom_change),
|
||||||
|
yoy_change: formatDecimal(plainManufacturing.yoy_change),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format ISIC 2-digit data
|
||||||
|
const formattedIsic2Digit = isic2DigitData.map(item => {
|
||||||
|
const plainItem = item.get({ plain: true });
|
||||||
|
return {
|
||||||
|
isic_2digit_code: plainItem.isic_2digit_code,
|
||||||
|
isic_description: plainItem.isic_description,
|
||||||
|
total_weight: formatDecimal(plainItem.total_weight),
|
||||||
|
weighted_index_sum: formatDecimal(plainItem.weighted_index_sum),
|
||||||
|
isic_2digit_index: formatDecimal(plainItem.isic_2digit_index),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Format ISIC 3-digit data
|
||||||
|
const formattedIsic3Digit = isic3DigitData.map(item => {
|
||||||
|
const plainItem = item.get({ plain: true });
|
||||||
|
return {
|
||||||
|
isic_3digit_code: plainItem.isic_3digit_code,
|
||||||
|
isic_description: plainItem.isic_description,
|
||||||
|
total_weight: formatDecimal(plainItem.total_weight),
|
||||||
|
weighted_index_sum: formatDecimal(plainItem.weighted_index_sum),
|
||||||
|
isic_3digit_index: formatDecimal(plainItem.isic_3digit_index),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Format ISIC 4-digit data
|
||||||
|
const formattedIsic4Digit = isic4DigitData.map(item => {
|
||||||
|
const plainItem = item.get({ plain: true });
|
||||||
|
return {
|
||||||
|
isic_4digit_code: plainItem.isic_4digit_code,
|
||||||
|
isic_description: plainItem.isic_description,
|
||||||
|
total_weight: formatDecimal(plainItem.total_weight),
|
||||||
|
weighted_index_sum: formatDecimal(plainItem.weighted_index_sum),
|
||||||
|
isic_4digit_index: formatDecimal(plainItem.isic_4digit_index),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`getManufacturingMonthlyOverview API: Successfully fetched data for year ${yearInt}, month ${monthInt}`);
|
||||||
|
|
||||||
|
res.status(200).send({
|
||||||
|
status: "success",
|
||||||
|
message: "Fetched successfully",
|
||||||
|
manufacturing_total: formattedManufacturing,
|
||||||
|
isic_2digit: formattedIsic2Digit,
|
||||||
|
isic_3digit: formattedIsic3Digit,
|
||||||
|
isic_4digit: formattedIsic4Digit
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`getManufacturingMonthlyOverview API Error: ${error.message}`);
|
||||||
|
logger.error(`Stack trace: ${error.stack}`);
|
||||||
|
res.status(500).send({
|
||||||
|
status: "failed",
|
||||||
|
message: "Internal server error"
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@ -118,7 +118,7 @@ exports.getAllProducts = async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(err.message);
|
logger.error(error.message);
|
||||||
res.status(500).send({ status: "failed", message: "Internal server error" });
|
res.status(500).send({ status: "failed", message: "Internal server error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -133,7 +133,7 @@ exports.getProductById = async (req, res) => {
|
|||||||
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
|
res.status(200).send({'status':"success",'message':"Fetched successfully",'data': data });
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(err.message);
|
logger.error(error.message);
|
||||||
res.status(500).send({'status':"failed",'message': "Internal server error" });
|
res.status(500).send({'status':"failed",'message': "Internal server error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -170,7 +170,7 @@ exports.deleteProduct = async (req, res) => {
|
|||||||
res.status(200).send({'status':"success",'message':"Deleted successfully",'data': "" });
|
res.status(200).send({'status':"success",'message':"Deleted successfully",'data': "" });
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(err.message);
|
logger.error(error.message);
|
||||||
res.status(500).send({'status':"failed",'message': "Internal server error" });
|
res.status(500).send({'status':"failed",'message': "Internal server error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -181,7 +181,7 @@ exports.downloadProductSample = async (req, res) => {
|
|||||||
const filePath = path.join(__dirname, "../downloads_csv/products_upload_sample.csv");
|
const filePath = path.join(__dirname, "../downloads_csv/products_upload_sample.csv");
|
||||||
return res.download(filePath, "products_upload_sample.csv");
|
return res.download(filePath, "products_upload_sample.csv");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(err.message);
|
logger.error(error.message);
|
||||||
res.status(500).send({'status':"failed",'message': "Internal server error" });
|
res.status(500).send({'status':"failed",'message': "Internal server error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -30,7 +30,9 @@ db.Emirate = require("./emirate.model")(sequelize, DataTypes);
|
|||||||
db.CityTown = require("./cityTown.model")(sequelize, DataTypes);
|
db.CityTown = require("./cityTown.model")(sequelize, DataTypes);
|
||||||
db.SubmissionHistory = require("./submissionHistory.model")(sequelize, DataTypes);
|
db.SubmissionHistory = require("./submissionHistory.model")(sequelize, DataTypes);
|
||||||
db.ManufacturingIpi = require("./manufacturingIPI.model.js")(sequelize, DataTypes);
|
db.ManufacturingIpi = require("./manufacturingIPI.model.js")(sequelize, DataTypes);
|
||||||
|
db.Isic2DigitIndices = require("./isic2DigitIndices.model.js")(sequelize, DataTypes);
|
||||||
|
db.Isic3DigitIndices = require("./isic3DigitIndices.model.js")(sequelize, DataTypes);
|
||||||
|
db.Isic4DigitIndices = require("./isic4DigitIndices.model.js")(sequelize, DataTypes);
|
||||||
// Associations
|
// Associations
|
||||||
db.Establishment.hasMany(db.EstablishmentUser, {
|
db.Establishment.hasMany(db.EstablishmentUser, {
|
||||||
foreignKey: "establishment_id",
|
foreignKey: "establishment_id",
|
||||||
|
|||||||
59
app/models/isic2DigitIndices.model.js
Normal file
59
app/models/isic2DigitIndices.model.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
const Isic2DigitIndices = sequelize.define("isic_2digit_indices", {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
isic_2digit_code: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
isic_description: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
validate: {
|
||||||
|
len: {
|
||||||
|
args: [0, 1000],
|
||||||
|
msg: "Description is too long. Maximum allowed length is 1000 characters."
|
||||||
|
}}
|
||||||
|
},
|
||||||
|
year: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
month: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
month_name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
total_weight: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
weighted_index_sum: {
|
||||||
|
type: DataTypes.DECIMAL(10, 4),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
isic_2digit_index: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
defaultValue: DataTypes.NOW,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
tableName: "isic_2digit_indices",
|
||||||
|
timestamps: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Isic2DigitIndices;
|
||||||
|
};
|
||||||
59
app/models/isic3DigitIndices.model.js
Normal file
59
app/models/isic3DigitIndices.model.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
const Isic3DigitIndices = sequelize.define("isic_3digit_indices", {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
isic_3digit_code: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
isic_description: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
validate: {
|
||||||
|
len: {
|
||||||
|
args: [0, 1000],
|
||||||
|
msg: "Description is too long. Maximum allowed length is 1000 characters."
|
||||||
|
}}
|
||||||
|
},
|
||||||
|
year: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
month: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
month_name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
total_weight: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
weighted_index_sum: {
|
||||||
|
type: DataTypes.DECIMAL(10, 4),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
isic_3digit_index: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
defaultValue: DataTypes.NOW,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
tableName: "isic_3digit_indices",
|
||||||
|
timestamps: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Isic3DigitIndices;
|
||||||
|
};
|
||||||
59
app/models/isic4DigitIndices.model.js
Normal file
59
app/models/isic4DigitIndices.model.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
const Isic4DigitIndices = sequelize.define("isic_4digit_indices", {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
isic_4digit_code: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
isic_description: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
validate: {
|
||||||
|
len: {
|
||||||
|
args: [0, 1000],
|
||||||
|
msg: "Description is too long. Maximum allowed length is 1000 characters."
|
||||||
|
}}
|
||||||
|
},
|
||||||
|
year: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
month: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
month_name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
total_weight: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
weighted_index_sum: {
|
||||||
|
type: DataTypes.DECIMAL(10, 4),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
isic_4digit_index: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
defaultValue: DataTypes.NOW,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
tableName: "isic_4digit_indices",
|
||||||
|
timestamps: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Isic4DigitIndices;
|
||||||
|
};
|
||||||
@ -3013,6 +3013,41 @@ router.get("/unit_master_download_sample_file",[verifySignature, verifyToken], u
|
|||||||
*/
|
*/
|
||||||
router.get("/manufacturing/getManufacturingIndex",[verifySignature, verifyToken], ManufacturingIpiController.getAllManufacturingIndexDetails);
|
router.get("/manufacturing/getManufacturingIndex",[verifySignature, verifyToken], ManufacturingIpiController.getAllManufacturingIndexDetails);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/manufacturing/getManufacturingMonthlyOverview
|
||||||
|
* get:
|
||||||
|
* summary: Get Manufacturing IPI Monthly Overview by year and month
|
||||||
|
* tags: [ManufacturingIPI]
|
||||||
|
* security:
|
||||||
|
* - appSignature: []
|
||||||
|
* - CSRF: []
|
||||||
|
* cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - name: year
|
||||||
|
* in: query
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Year (e.g., 2025)
|
||||||
|
* - name: month
|
||||||
|
* in: query
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Month (1-12)
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Manufacturing IPI Monthly Overview fetched successfully
|
||||||
|
* 400:
|
||||||
|
* description: Invalid parameters
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Server error
|
||||||
|
*/
|
||||||
|
router.get("/manufacturing/getManufacturingMonthlyOverview", [verifySignature, verifyToken], ManufacturingIpiController.getManufacturingMonthlyOverviewByYearMonth);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user