report download

This commit is contained in:
Sibunnayak 2024-11-06 14:05:16 +05:30
parent 8222e21770
commit 132d155999
13 changed files with 2667 additions and 90 deletions

4
app.js
View File

@ -210,6 +210,8 @@ import AnnouncementRoute from './resources/Announcement/announcementRouter.js'
import Stock from "./resources/Stock/StockRoute.js";
//Reports
import Report from "./resources/Reports/ReportRoute.js";
//Mobileapp
import MobileAppRoute from "./resources/setting/MobileApp/MobileAppRoute.js";
app.use("/api/v1", user);
//Product
@ -317,4 +319,6 @@ app.use("/api", Stock);
// app.use("/api", RegisterEmail);
//Reports
app.use("/api/report",Report);
//MobileApp
app.use("/api/mobileapp", MobileAppRoute);
export default app;

2261
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,9 +22,11 @@
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"exceljs": "^4.4.0",
"express": "^4.18.1",
"express-fileupload": "^1.4.0",
"firebase-admin": "^12.3.0",
"firebase": "^11.0.1",
"firebase-admin": "^12.7.0",
"generate-password": "^1.7.0",
"jsonwebtoken": "^8.5.1",
"messagebird": "^4.0.1",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,7 @@ import mongoose from "mongoose";
import { Product } from "../Products/ProductModel.js";
import { PDStock } from "../Stock/PdStockModel.js";
import { RDStock } from "../Stock/RdStockModel.js";
import * as XLSX from "xlsx";
export const getProductsWithOpenInventoryInfo = async (req, res) => {
try {
@ -21,7 +22,7 @@ export const getProductsWithOpenInventoryInfo = async (req, res) => {
const products = await Product.find(searchCriteria)
.skip(skip)
.limit(limit)
.populate("brand", "brandName") // Populate brandName
.populate("brand", "brandName") // Populate brandName
.populate("category", "categoryName") // Populate categoryName
.exec();
@ -77,7 +78,7 @@ export const getProductsWithOpenInventoryInfo = async (req, res) => {
const productData = products.map((product) => ({
SKU: product.SKU,
name: product.name,
brand: product.brand?.brandName || "N/A", // Access brandName here
brand: product.brand?.brandName || "N/A", // Access brandName here
category: product.category?.categoryName || "N/A", // Access categoryName here
allPDs: pdMap[product.SKU] || 0,
allRDs: rdMap[product.SKU] || 0,
@ -120,7 +121,7 @@ export const getProductsWithStockInfo = async (req, res) => {
const products = await Product.find(searchCriteria)
.skip(skip)
.limit(limit)
.populate("brand", "brandName") // Populate brandName
.populate("brand", "brandName") // Populate brandName
.populate("category", "categoryName") // Populate categoryName
.exec();
@ -176,7 +177,7 @@ export const getProductsWithStockInfo = async (req, res) => {
const productData = products.map((product) => ({
SKU: product.SKU,
name: product.name,
brand: product.brand?.brandName || "N/A", // Access brandName here
brand: product.brand?.brandName || "N/A", // Access brandName here
category: product.category?.categoryName || "N/A", // Access categoryName here
allPDs: pdMap[product.SKU] || 0,
allRDs: rdMap[product.SKU] || 0,
@ -200,3 +201,215 @@ export const getProductsWithStockInfo = async (req, res) => {
res.status(500).json({ success: false, message: error.message });
}
};
export const DownloadProductsWithOpenInventoryInfo = async (req, res) => {
try {
// Fetch all products with necessary population
const products = await Product.find()
.populate("brand", "brandName") // Populate brandName
.populate("category", "categoryName") // Populate categoryName
.exec();
// Collect all product SKUs
const productSKUs = products.map((product) => product.SKU);
// Fetch all PDs with non-zero opening inventory for these products using SKU
const pdOIs = await PDStock.aggregate([
{ $unwind: "$products" },
{
$match: {
"products.openingInventory": { $gt: 0 },
"products.SKU": { $in: productSKUs },
},
},
{
$group: {
_id: "$products.SKU",
totalPdOpeningInventory: { $sum: "$products.openingInventory" },
},
},
]);
// Fetch all RDs with non-zero opening inventory for these products using SKU
const rdOIs = await RDStock.aggregate([
{ $unwind: "$products" },
{
$match: {
"products.openingInventory": { $gt: 0 },
"products.SKU": { $in: productSKUs },
},
},
{
$group: {
_id: "$products.SKU",
totalRdOpeningInventory: { $sum: "$products.openingInventory" },
},
},
]);
// Prepare mappings of PD and RD opening inventories by SKU
const pdMap = {};
pdOIs.forEach((pd) => {
pdMap[pd._id] = pd.totalPdOpeningInventory;
});
const rdMap = {};
rdOIs.forEach((rd) => {
rdMap[rd._id] = rd.totalRdOpeningInventory;
});
// Combine product info with PD/RD counts using SKU
const productData = products.map((product) => ({
"SKU Code": product.SKU,
"SKU Description": product.name, // SKU Description
"Category Name": product.category?.categoryName || "N/A", // Category Name
"Brand Name": product.brand?.brandName || "N/A", // Brand Name
"Total At PDs And Retailers": (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0), // Total At PDs & Retailers
"All PDs": pdMap[product.SKU] || 0, // All PDs
"All Retailers": rdMap[product.SKU] || 0, // All Retailers
}));
// Define headers (first row in the sheet)
const headers = [
["SKU Code", "SKU Description", "Category Name", "Brand Name", "Total At PDs And Retailers", "All PDs", "All Retailers"]
];
// Create worksheet with headers
const worksheet = XLSX.utils.aoa_to_sheet(headers);
// Append product data below headers
XLSX.utils.sheet_add_json(worksheet, productData, { skipHeader: true, origin: "A2" });
// Calculate and set column widths based on content
const columnWidths = headers[0].map((header, index) => {
let maxLength = header.length; // Start with the length of the header
for (const row of productData) {
const cellContent = row[headers[0][index]]?.toString() || "";
maxLength = Math.max(maxLength, cellContent.length);
}
return { wch: maxLength + 2 }; // Add some padding
});
worksheet['!cols'] = columnWidths;
// Create workbook and append worksheet
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "OpeningInventoryReport");
// Write workbook to buffer
const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" });
// Send Excel file as response
res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
res.setHeader("Content-Disposition", "attachment; filename=OpeningInventoryReport.xlsx");
res.send(excelBuffer);
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
export const DownloadProductsWithStockInfo = async (req, res) => {
try {
// Fetch all products with necessary population
const products = await Product.find()
.populate("brand", "brandName") // Populate brandName
.populate("category", "categoryName") // Populate categoryName
.exec();
// Collect all product SKUs
const productSKUs = products.map((product) => product.SKU);
// Fetch all PDs with non-zero Stock for these products using SKU
const pdOIs = await PDStock.aggregate([
{ $unwind: "$products" },
{
$match: {
"products.Stock": { $gt: 0 },
"products.SKU": { $in: productSKUs },
},
},
{
$group: {
_id: "$products.SKU",
totalPdStock: { $sum: "$products.Stock" },
},
},
]);
// Fetch all RDs with non-zero Stockfor these products using SKU
const rdOIs = await RDStock.aggregate([
{ $unwind: "$products" },
{
$match: {
"products.Stock": { $gt: 0 },
"products.SKU": { $in: productSKUs },
},
},
{
$group: {
_id: "$products.SKU",
totalRdStock: { $sum: "$products.Stock" },
},
},
]);
// Prepare mappings of PD and RD Stocks by SKU
const pdMap = {};
pdOIs.forEach((pd) => {
pdMap[pd._id] = pd.totalPdStock;
});
const rdMap = {};
rdOIs.forEach((rd) => {
rdMap[rd._id] = rd.totalRdStock;
});
// Combine product info with PD/RD counts using SKU
const productData = products.map((product) => ({
"SKU Code": product.SKU,
"SKU Description": product.name, // SKU Description
"Category Name": product.category?.categoryName || "N/A", // Category Name
"Brand Name": product.brand?.brandName || "N/A", // Brand Name
"Total At PDs And Retailers": (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0), // Total At PDs & Retailers
"All PDs": pdMap[product.SKU] || 0, // All PDs
"All Retailers": rdMap[product.SKU] || 0, // All Retailers
}));
// Define headers (first row in the sheet)
const headers = [
["SKU Code", "SKU Description", "Category Name", "Brand Name", "Total At PDs And Retailers", "All PDs", "All Retailers"]
];
// Create worksheet with headers
const worksheet = XLSX.utils.aoa_to_sheet(headers);
// Append product data below headers
XLSX.utils.sheet_add_json(worksheet, productData, { skipHeader: true, origin: "A2" });
// Calculate and set column widths based on content
const columnWidths = headers[0].map((header, index) => {
let maxLength = header.length; // Start with the length of the header
for (const row of productData) {
const cellContent = row[headers[0][index]]?.toString() || "";
maxLength = Math.max(maxLength, cellContent.length);
}
return { wch: maxLength + 2 }; // Add some padding
});
worksheet['!cols'] = columnWidths;
// Create workbook and append worksheet
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "StockReport");
// Write workbook to buffer
const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" });
// Send Excel file as response
res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
res.setHeader("Content-Disposition", "attachment; filename=StockReport.xlsx");
res.send(excelBuffer);
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};

View File

@ -2,6 +2,8 @@ import express from "express";
import {
getProductsWithStockInfo,
getProductsWithOpenInventoryInfo,
DownloadProductsWithOpenInventoryInfo,
DownloadProductsWithStockInfo,
} from "./OpeningInventoryReports.js";
import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js";
@ -15,7 +17,20 @@ router
authorizeRoles("admin"),
getProductsWithOpenInventoryInfo
);
router
.route("/opening-inventory/download")
.get(
isAuthenticatedUser,
authorizeRoles("admin"),
DownloadProductsWithOpenInventoryInfo
);
router
.route("/stock/download")
.get(
isAuthenticatedUser,
authorizeRoles("admin"),
DownloadProductsWithStockInfo
);
router
.route("/stock")
.get(isAuthenticatedUser, authorizeRoles("admin"), getProductsWithStockInfo);

View File

@ -0,0 +1,185 @@
import MobileApp from "./MobileAppModel.js";
import { ref, uploadBytes, getDownloadURL, deleteObject } from "firebase/storage";
import { storage } from "../../../uploads/firebaseConfig.js";
export const addMobileApp = async (req, res) => {
try {
const application = await MobileApp.findOne();
const folderPath = "MobileApps/";
let pdapp, rdapp, scapp, tmapp;
// Helper function to upload and get `url` for an APK
const uploadApp = async (file, currentPublicId) => {
// Delete old file if it exists
if (currentPublicId) {
const oldFileRef = ref(storage, `${folderPath}${currentPublicId}`);
await deleteObject(oldFileRef);
}
// Create a storage reference
const storageRef = ref(storage, `${folderPath}${file.name}`);
// Upload the file
await uploadBytes(storageRef, file.data);
// Get the download URL
const url = await getDownloadURL(storageRef);
return { public_id: file.name, url }; // Use file.name as public_id
};
// Upload APKs conditionally based on their presence in `req.files`
if (req.files.PDApp) {
pdapp = await uploadApp(req.files.PDApp, application?.PDApp?.public_id);
}
if (req.files.RDApp) {
rdapp = await uploadApp(req.files.RDApp, application?.RDApp?.public_id);
}
if (req.files.SCApp) {
scapp = await uploadApp(req.files.SCApp, application?.SCApp?.public_id);
}
if (req.files.TMApp) {
tmapp = await uploadApp(req.files.TMApp, application?.TMApp?.public_id);
}
// Prepare update object only for fields that have new uploads
const updateData = {
...(pdapp && { PDApp: pdapp }),
...(rdapp && { RDApp: rdapp }),
...(scapp && { SCApp: scapp }),
...(tmapp && { TMApp: tmapp }),
};
if (!application) {
// Create new record if it doesn't exist
const newApplication = await MobileApp.create(updateData);
return res.status(200).json({
status: "success",
message: "Uploaded Apps Successfully",
PDAppUrl: newApplication.PDApp?.url,
RDAppUrl: newApplication.RDApp?.url,
SCAppUrl: newApplication.SCApp?.url,
TMAppUrl: newApplication.TMApp?.url,
});
} else {
// Update existing record
await MobileApp.updateOne({}, { $set: updateData });
return res.status(200).json({
status: "success",
message: "Updated Apps Successfully",
PDAppUrl: pdapp?.url || application.PDApp.url,
RDAppUrl: rdapp?.url || application.RDApp.url,
SCAppUrl: scapp?.url || application.SCApp.url,
TMAppUrl: tmapp?.url || application.TMApp.url,
});
}
} catch (error) {
console.error("Error uploading APKs:", error);
return res.status(500).json({
status: "error",
message: "An error occurred while uploading APKs.",
});
}
};
export const getMobileApp = async (req, res) => {
try {
const application = await MobileApp.findOne();
if (!application) {
return res.status(404).json({
status: "error",
message: "No Mobile App found",
});
}
return res.status(200).json({
status: "success",
PDAppUrl: application.PDApp?.url,
RDAppUrl: application.RDApp?.url,
SCAppUrl: application.SCApp?.url,
TMAppUrl: application.TMApp?.url,
});
} catch (error) {
console.error("Error fetching Mobile App:", error);
return res.status(500).json({
status: "error",
message: "An error occurred while fetching Mobile App.",
});
}
};
// import cloudinary from "../../../Utils/cloudinary.js";
// export const addMobileApp = async (req, res) => {
// try {
// const application = await MobileApp.findOne();
// const folderPath = "MobileApps";
// let pdapp, rdapp, scapp, tmapp;
// console.log("1", req.files);
// console.log("3", application);
// // Helper function to upload and get `url` and `public_id` for an APK
// const uploadApp = async (file, currentPublicId) => {
// // Delete old file if it exists
// if (currentPublicId) {
// await cloudinary.v2.uploader.destroy(currentPublicId);
// }
// // Upload new file
// const result = await cloudinary.v2.uploader.upload(file.tempFilePath, {
// folder: folderPath,
// resource_type: "auto",
// });
// return { public_id: result.public_id, url: result.secure_url };
// };
// // Upload APKs conditionally based on their presence in `req.files`
// if (req.files.PDApp) {
// pdapp = await uploadApp(req.files.PDApp, application?.PDApp?.public_id);
// }
// if (req.files.RDApp) {
// rdapp = await uploadApp(req.files.RDApp, application?.RDApp?.public_id);
// }
// if (req.files.SCApp) {
// scapp = await uploadApp(req.files.SCApp, application?.SCApp?.public_id);
// }
// if (req.files.TMApp) {
// tmapp = await uploadApp(req.files.TMApp, application?.TMApp?.public_id);
// }
// // Prepare update object only for fields that have new uploads
// const updateData = {
// ...(pdapp && { PDApp: pdapp }),
// ...(rdapp && { RDApp: rdapp }),
// ...(scapp && { SCApp: scapp }),
// ...(tmapp && { TMApp: tmapp }),
// };
// if (!application) {
// // Create new record if it doesn't exist
// const newApplication = await MobileApp.create(updateData);
// return res.status(200).json({
// status: "success",
// message: "Uploaded Apps Successfully",
// PDAppUrl: newApplication.PDApp?.url,
// RDAppUrl: newApplication.RDApp?.url,
// SCAppUrl: newApplication.SCApp?.url,
// TMAppUrl: newApplication.TMApp?.url,
// });
// } else {
// // Update existing record
// await MobileApp.updateOne({}, { $set: updateData });
// return res.status(200).json({
// status: "success",
// message: "Updated Apps Successfully",
// PDAppUrl: pdapp?.url || application.PDApp.url,
// RDAppUrl: rdapp?.url || application.RDApp.url,
// SCAppUrl: scapp?.url || application.SCApp.url,
// TMAppUrl: tmapp?.url || application.TMApp.url,
// });
// }
// } catch (error) {
// console.error("Error uploading APKs:", error);
// return res.status(500).json({
// status: "error",
// message: "An error occurred while uploading APKs.",
// });
// }
// };

View File

@ -0,0 +1,28 @@
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const MobileAppSchema = new Schema(
{
PDApp: {
public_id: String,
url: String,
},
RDApp: {
public_id: String,
url: String,
},
SCApp: {
public_id: String,
url: String,
},
TMApp: {
public_id: String,
url: String,
},
},
{ timestamps: true }
);
const MobileApp = model("MobileApp", MobileAppSchema);
export default MobileApp;

View File

@ -0,0 +1,15 @@
import express from "express";
import { getMobileApp, addMobileApp } from "./MobileAppController.js";
import {
isAuthenticatedUser,
authorizeRoles,
} from "../../../middlewares/auth.js";
const router = express.Router();
router
.route("/add")
.post(isAuthenticatedUser, authorizeRoles("admin", "Employee"), addMobileApp);
router.route("/getall").get(getMobileApp);
export default router;

20
uploads/firebaseConfig.js Normal file
View File

@ -0,0 +1,20 @@
// firebaseConfig.js
import { initializeApp } from "firebase/app";
import { getStorage } from "firebase/storage";
// Your web app's Firebase configuration (Make sure this is securely managed)
const firebaseConfig = {
apiKey: "AIzaSyAcLLGSkTgqK-mGCTPyTMTGxhbVodk8RtY",
authDomain: "cheminova-mobile-app.firebaseapp.com",
projectId: "cheminova-mobile-app",
storageBucket: "cheminova-mobile-app.appspot.com",
messagingSenderId: "965473183668",
appId: "1:965473183668:web:867f2f50797b0e91c074e1",
measurementId: "G-SXXNTWFRSP"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const storage = getStorage(app);
export { storage }; // Export the storage instance