Merge branch 'dev' of https://github.com/kfnawaz/plaidware-wms-core into dev
This commit is contained in:
@@ -2,7 +2,7 @@ require("dotenv").config();
|
||||
|
||||
const envVariables = {
|
||||
API_PORT: process.env.API_PORT || "3000",
|
||||
MONGODB_URI: process.env.MONGODB_URI || "mongodb://localhost:12017",
|
||||
MONGODB_URI: process.env.MONGODB_URI || "mongodb://localhost:27017",
|
||||
JWT_SECRET: process.env.JWT_SECRET || "secret123",
|
||||
JWT_REFRESH_EXPIRY_TIME: parseInt(process.env.JWT_REFRESH_EXPIRY_TIME) || 3600,
|
||||
JWT_ACCESS_EXPIRY_TIME: parseInt(process.env.JWT_ACCESS_EXPIRY_TIME) || 86400,
|
||||
|
||||
@@ -30,7 +30,6 @@ module.exports = {
|
||||
res.status(404).send({ success: false, error: "Inventory not found" });
|
||||
return;
|
||||
}
|
||||
if (inventoryData.image_url) inventoryData.image_url = S3.generatePresignedUrl(inventoryData.image_url);
|
||||
res.send({ success: true, data: inventoryData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -53,7 +52,6 @@ module.exports = {
|
||||
|
||||
for (const inventory of inventoryData) {
|
||||
inventory["widgetFamilies"] = await WidgetFamily.find({ inventory: inventory._id });
|
||||
if (inventory.image_url) inventory.image_url = S3.generatePresignedUrl(inventory.image_url);
|
||||
}
|
||||
|
||||
res.send({ success: true, data: inventoryData });
|
||||
@@ -67,7 +65,6 @@ module.exports = {
|
||||
*/
|
||||
createInventory: async (req, res, next) => {
|
||||
let { name, policies, widgetName, icon_slug } = req.body;
|
||||
const image = req.file;
|
||||
|
||||
if (!(name && widgetName && icon_slug)) {
|
||||
res.status(400).send("Missing params");
|
||||
@@ -98,17 +95,11 @@ module.exports = {
|
||||
icon_slug,
|
||||
});
|
||||
|
||||
if (image) {
|
||||
const url = await S3.uploadFile(`inventory/${inventoryData._id.toString()}.${image.originalname.split(".").slice(-1).pop()}`, image.path);
|
||||
inventoryData.image_url = url;
|
||||
}
|
||||
|
||||
await inventoryData.save();
|
||||
if (!inventoryData) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
await inventoryData.save();
|
||||
res.send({ success: true, data: { inventoryData } });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -160,12 +151,6 @@ module.exports = {
|
||||
};
|
||||
|
||||
inventory.policies = verifiedPolicies;
|
||||
const image = req.file;
|
||||
if (image) {
|
||||
const url = await S3.uploadFile(`inventory/${inventory._id.toString()}.${image.originalname.split(".").slice(-1).pop()}`, image.path);
|
||||
inventory.image_url = url;
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.send({ success: true, data: { inventory } });
|
||||
} catch (error) {
|
||||
@@ -205,4 +190,32 @@ module.exports = {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch all available inventories
|
||||
*/
|
||||
getInventories: async (req, res, next) => {
|
||||
let { page, perPage } = req.query;
|
||||
page = page ? parseInt(page) : 0;
|
||||
perPage = perPage ? parseInt(perPage) : 10;
|
||||
try {
|
||||
const inventoryData = await Inventory.find(
|
||||
{ },
|
||||
{ id: 1, name: 1, type: 1 },
|
||||
{ skip: parseInt(page) * parseInt(perPage), limit: parseInt(perPage) }
|
||||
);
|
||||
if (!inventoryData) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const inventory of inventoryData) {
|
||||
inventory["widgetFamilies"] = await WidgetFamily.find({ inventory: inventory._id });
|
||||
}
|
||||
|
||||
res.send({ success: true, data: inventoryData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,6 +27,11 @@ router.get("/all", controller.getInventories);
|
||||
*/
|
||||
router.get("/filter-by-type", controller.getInventoryByType);
|
||||
|
||||
/**
|
||||
* @route /inventory/all
|
||||
*/
|
||||
router.get("/all", controller.getInventories);
|
||||
|
||||
/**
|
||||
* @route /inventory/:id
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const mongoose = require("mongoose");
|
||||
const Item = require("../models/Item");
|
||||
const WidgetFamily = require("../models/WidgetFamily");
|
||||
const Inventory = require("../models/Inventory");
|
||||
const {
|
||||
PickItemTransaction,
|
||||
PutItemTransaction,
|
||||
@@ -14,8 +13,8 @@ const {
|
||||
const { S3 } = require("./../config/aws");
|
||||
const ItemAssociation = require("../models/ItemAssociation");
|
||||
const Sublevel = require("../models/Sublevel");
|
||||
const { InventoryTypes, ReportItemForTypes } = require("../config/constants");
|
||||
|
||||
const { ReportItemForTypes } = require("../config/constants");
|
||||
const { filterItems, filterItemAssociations } = require("./utils/aggregation");
|
||||
module.exports = {
|
||||
/**
|
||||
* Gets the Item data by `id`
|
||||
@@ -24,16 +23,22 @@ module.exports = {
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id) {
|
||||
res.status(400).send("Missing id param");
|
||||
res.status(400).send({ success: false, error: "Missing id param" });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const itemData = await Item.findById(id).populate({ path: "widgetFamily", populate: "inventory" });
|
||||
if (!itemData) {
|
||||
res.status(404);
|
||||
res.status(404).send({ success: false, error: "Item not found" });
|
||||
return;
|
||||
}
|
||||
if (itemData.images && itemData.images.length > 0) {
|
||||
itemData.images = itemData.images.map((_) => {
|
||||
return { _id: _._id, url: S3.generatePresignedUrl(_.url) };
|
||||
});
|
||||
}
|
||||
|
||||
res.send({ success: true, data: itemData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -95,11 +100,16 @@ module.exports = {
|
||||
`item/${itemData._id.toString()}-${Date.now()}-${i}.${images[i].originalname.split(".").slice(-1).pop()}`,
|
||||
images[i].path
|
||||
);
|
||||
itemData.images ||= [];
|
||||
itemData.images = itemData.images || [];
|
||||
itemData.images.push({ url });
|
||||
}
|
||||
itemData.save();
|
||||
|
||||
if (itemData.images) {
|
||||
itemData.images = itemData.images.map((_) => {
|
||||
return { _id: _._id, url: S3.generatePresignedUrl(_.url) };
|
||||
});
|
||||
}
|
||||
res.send({ success: true, data: itemData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -150,8 +160,32 @@ module.exports = {
|
||||
itemData[key] = item[key];
|
||||
}
|
||||
}
|
||||
// Removal of images
|
||||
const existingImageIds = req.body.imageIds || [];
|
||||
itemData.images = itemData.images.filter((image) => {
|
||||
if (!image) return false;
|
||||
return existingImageIds.includes(image._id.toString());
|
||||
});
|
||||
// Addition of images
|
||||
const images = req.files;
|
||||
if (images && Array.isArray(images)) {
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
const url = await S3.uploadFile(
|
||||
`item/${itemData._id.toString()}-${Date.now()}-${i}.${images[i].originalname.split(".").slice(-1).pop()}`,
|
||||
images[i].path
|
||||
);
|
||||
itemData.images ||= [];
|
||||
itemData.images.push({ url });
|
||||
}
|
||||
}
|
||||
|
||||
await itemData.save();
|
||||
|
||||
if (itemData.images) {
|
||||
itemData.images = itemData.images.map((_) => {
|
||||
return { _id: _._id, url: S3.generatePresignedUrl(_.url) };
|
||||
});
|
||||
}
|
||||
res.send({ success: true, data: itemData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -162,75 +196,39 @@ module.exports = {
|
||||
* Gets the Items data by filter
|
||||
*/
|
||||
getItemsByFilter: async (req, res, next) => {
|
||||
let { family, type, inventory, page, perPage } = req.query;
|
||||
let { family, inventory, page, perPage } = req.query;
|
||||
page = page ? parseInt(page) : 0;
|
||||
perPage = perPage ? parseInt(perPage) : 10;
|
||||
const inventoryFilters = {};
|
||||
let inventories;
|
||||
let widgetFamilies;
|
||||
let itemFilters;
|
||||
try {
|
||||
if (type && InventoryTypes.includes(type)) {
|
||||
inventoryFilters["type"] = type;
|
||||
}
|
||||
const results = await filterItems(inventory, family, page, perPage);
|
||||
|
||||
if (inventory) {
|
||||
inventoryFilters["_id"] = inventory;
|
||||
for (const item of results[0].result) {
|
||||
if (item.images) {
|
||||
item.images = item.images.map((_) => {
|
||||
return { _id: _._id, url: S3.generatePresignedUrl(_.url) };
|
||||
});
|
||||
}
|
||||
}
|
||||
res.send({ success: true, data: results[0] });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
getItemAssociationsByFilter: async (req, res, next) => {
|
||||
let { family, inventory, page, perPage } = req.query;
|
||||
page = page ? parseInt(page) : 0;
|
||||
perPage = perPage ? parseInt(perPage) : 10;
|
||||
try {
|
||||
const results = await filterItemAssociations(inventory, family, page, perPage);
|
||||
|
||||
if (Object.keys(inventoryFilters).length > 0) {
|
||||
inventories = await Inventory.find(inventoryFilters);
|
||||
for (const item of results[0].result) {
|
||||
if (item.images) {
|
||||
item.images = item.images.map((_) => {
|
||||
return { _id: _._id, url: S3.generatePresignedUrl(_.url) };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const widgetFamilyFilters = [];
|
||||
if (inventories) {
|
||||
widgetFamilyFilters.push({
|
||||
inventory: { $in: inventories.map((_) => _._id) },
|
||||
});
|
||||
}
|
||||
|
||||
if (family) {
|
||||
widgetFamilyFilters.push({
|
||||
name: family,
|
||||
});
|
||||
}
|
||||
if (widgetFamilyFilters.length > 0) {
|
||||
widgetFamilies = await WidgetFamily.find({
|
||||
$or: widgetFamilyFilters,
|
||||
});
|
||||
}
|
||||
|
||||
if (widgetFamilies) {
|
||||
itemFilters = { widgetFamily: { $in: widgetFamilies.map((_) => _._id) } };
|
||||
} else {
|
||||
itemFilters = {};
|
||||
}
|
||||
const itemData = await Item.find(
|
||||
itemFilters,
|
||||
{
|
||||
id: 1,
|
||||
commonName: 1,
|
||||
formalName: 1,
|
||||
description: 1,
|
||||
manufacturer: 1,
|
||||
widgetFamily: 1,
|
||||
size: 1,
|
||||
color: 1,
|
||||
type: 1,
|
||||
unitOfMaterial: 1,
|
||||
unitCost: 1,
|
||||
packageCount: 1,
|
||||
countPerPallet: 1,
|
||||
countPerPalletPackage: 1,
|
||||
customAttributes: 1,
|
||||
},
|
||||
{ skip: page * perPage, limit: perPage }
|
||||
).populate({ path: "widgetFamily" });
|
||||
if (!itemData) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
res.send({ success: true, data: itemData });
|
||||
res.send({ success: true, data: results[0] });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -526,10 +524,14 @@ module.exports = {
|
||||
|
||||
const image = req.file;
|
||||
const url = await S3.uploadFile(`item/${item._id.toString()}-${Date.now()}-0.${image.originalname.split(".").slice(-1).pop()}`, image.path);
|
||||
item.images ||= [];
|
||||
item.images = item.images || [];
|
||||
item.images.push({ url });
|
||||
await item.save();
|
||||
|
||||
if (item.images) {
|
||||
item.images = item.images.map((_) => {
|
||||
return { _id: _._id, url: S3.generatePresignedUrl(_.url) };
|
||||
});
|
||||
}
|
||||
res.send({ success: true, data: item });
|
||||
},
|
||||
|
||||
@@ -548,10 +550,16 @@ module.exports = {
|
||||
}
|
||||
|
||||
item.images = item.images.filter((itemImage) => {
|
||||
return (itemImage._id.toString() != image_id);
|
||||
return itemImage._id.toString() != image_id;
|
||||
});
|
||||
|
||||
await item.save();
|
||||
|
||||
if (item.images) {
|
||||
item.images = item.images.map((_) => {
|
||||
return { _id: _._id, url: S3.generatePresignedUrl(_.url) };
|
||||
});
|
||||
}
|
||||
res.send({ success: true, data: item });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -22,13 +22,18 @@ router.delete("/:id/image/:image_id", controller.removeImageFromItem);
|
||||
/**
|
||||
* @route /item/
|
||||
*/
|
||||
router.patch("/:id", controller.updateItemByID);
|
||||
router.patch("/:id", upload.any("images"), controller.updateItemByID);
|
||||
|
||||
/**
|
||||
* @route /item/filter
|
||||
*/
|
||||
router.get("/filter", controller.getItemsByFilter);
|
||||
|
||||
/**
|
||||
* @route /item/filter-association
|
||||
*/
|
||||
router.get("/filter-association", controller.getItemAssociationsByFilter);
|
||||
|
||||
/**
|
||||
* @route /item/:id
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ const { JWT_SECRET, JWT_REFRESH_EXPIRY_TIME, JWT_ACCESS_EXPIRY_TIME } = require(
|
||||
const UserRole = require("../models/UserRole");
|
||||
const { getScopes } = require("./utils/access-control");
|
||||
const { InventoryScopes, WarehouseScopes, UserActions, AllUIModules } = require("./../config/constants");
|
||||
const { S3 } = require("./../config/aws");
|
||||
|
||||
const createAccessToken = (id) => {
|
||||
return jwt.sign({ id }, JWT_SECRET, {
|
||||
@@ -206,7 +207,11 @@ module.exports = {
|
||||
.limit(perPage)
|
||||
.populate({ path: "roles", populate: "permissions" })
|
||||
.populate("permissions")
|
||||
.populate("createdBy");
|
||||
.populate("createdBy")
|
||||
.populate("updatedBy");
|
||||
for (const user of result) {
|
||||
if (user.image_url) user.image_url = S3.generatePresignedUrl(user.image_url);
|
||||
}
|
||||
res.send({ success: true, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -220,32 +225,39 @@ module.exports = {
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await User.findOne({ _id: id }, { id: 1, fullName: 1, email: 1, roles: 1, permissions: 1, createdBy: 1 })
|
||||
.populate({ path: "roles", populate: "permissions" })
|
||||
.populate("permissions")
|
||||
const result = await User.findOne({ _id: id })
|
||||
.populate("roles")
|
||||
.populate("createdBy");
|
||||
if (result._doc.image_url) result.image_url = S3.generatePresignedUrl(result.image_url);
|
||||
res.send({ success: true, data: result });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
createUser: async (req, res, next) => {
|
||||
const {
|
||||
email,
|
||||
let {
|
||||
fullName,
|
||||
email,
|
||||
password,
|
||||
phoneNumber,
|
||||
roles,
|
||||
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
|
||||
permissions,
|
||||
} = req.body;
|
||||
permissions = permissions || {};
|
||||
const { inventoryScopes, warehouseScopes, actions, allowedUIModules } = permissions;
|
||||
|
||||
try {
|
||||
const salt = await bcrypt.genSalt();
|
||||
const passwordEncrypted = password && await bcrypt.hash(password, salt);
|
||||
const newUser = {
|
||||
email: email,
|
||||
fullName: fullName,
|
||||
password: await bcrypt.hash(password, salt),
|
||||
fullName,
|
||||
email,
|
||||
phoneNumber,
|
||||
isActive: true,
|
||||
createdBy: res.locals.user,
|
||||
createdAt: new Date()
|
||||
};
|
||||
passwordEncrypted && (newUser.password = passwordEncrypted);
|
||||
|
||||
if (roles) {
|
||||
let verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
@@ -270,8 +282,13 @@ module.exports = {
|
||||
}
|
||||
const user = await User.create(newUser);
|
||||
console.log({ msg: "new user created", user });
|
||||
|
||||
res.send({ success: true, data: user });
|
||||
const image = req.file;
|
||||
if (image) {
|
||||
const url = await S3.uploadFile(`user/${user._id.toString()}.${image.originalname.split(".").slice(-1).pop()}`, image.path);
|
||||
user.image_url = url;
|
||||
await user.save();
|
||||
}
|
||||
res.send({ success: true, data: { ...user, image_url: S3.generatePresignedUrl(user.image_url) } });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
next(err);
|
||||
@@ -284,14 +301,18 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
email,
|
||||
fullName,
|
||||
password,
|
||||
roles,
|
||||
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
|
||||
} = req.body;
|
||||
try {
|
||||
let {
|
||||
fullName,
|
||||
email,
|
||||
password,
|
||||
phoneNumber,
|
||||
roles,
|
||||
isActive,
|
||||
permissions
|
||||
} = req.body;
|
||||
permissions = permissions || {};
|
||||
const { inventoryScopes, warehouseScopes, actions, allowedUIModules } = permissions;
|
||||
const user = await User.findById(id);
|
||||
if (!user) {
|
||||
res.status(404).send({ success: false, error: "User not found" });
|
||||
@@ -299,9 +320,13 @@ module.exports = {
|
||||
}
|
||||
const salt = await bcrypt.genSalt();
|
||||
|
||||
if (email) user.email = email;
|
||||
if (fullName) user.fullName = fullName;
|
||||
if (email) user.email = email;
|
||||
if (password) user.password = await bcrypt.hash(password, salt);
|
||||
if (phoneNumber) user.phoneNumber = phoneNumber;
|
||||
if (isActive !== undefined) user.isActive = isActive;
|
||||
user.updatedBy = res.locals.user;
|
||||
user.updatedAt = new Date();
|
||||
if (roles) {
|
||||
let verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
verifiedRoleIds = verifiedRoleIds || [];
|
||||
@@ -326,9 +351,15 @@ module.exports = {
|
||||
user.markModified("permissions.allowedUIModules");
|
||||
}
|
||||
|
||||
const image = req.file;
|
||||
if (image) {
|
||||
const url = await S3.uploadFile(`user/${user._id.toString()}.${image.originalname.split(".").slice(-1).pop()}`, image.path);
|
||||
user.image_url = url;
|
||||
}
|
||||
|
||||
await user.save();
|
||||
|
||||
res.send({ success: true, data: user });
|
||||
res.send({ success: true, data: { ...user, image_url: S3.generatePresignedUrl(user.image_url) } });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
next(err);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const router = require("express").Router();
|
||||
const controller = require("./user.controller");
|
||||
const { SuperAdminCheck, AuthenticateMiddleware } = require("./utils/authorize");
|
||||
const multer = require("multer");
|
||||
const upload = multer({ dest: "tmp/uploads/" });
|
||||
|
||||
|
||||
router.post("/register", controller.registerUser);
|
||||
router.post("/login", controller.loginUser);
|
||||
@@ -11,7 +14,7 @@ router.get("/allowed-ui-modules", AuthenticateMiddleware, controller.getUIAccess
|
||||
|
||||
router.get("/all", AuthenticateMiddleware, SuperAdminCheck, controller.getAllUsers);
|
||||
router.get("/:id", AuthenticateMiddleware, SuperAdminCheck, controller.getUserById);
|
||||
router.post("/create", AuthenticateMiddleware, SuperAdminCheck, controller.createUser);
|
||||
router.post("/:id", AuthenticateMiddleware, SuperAdminCheck, controller.updateUser);
|
||||
router.post("/create", upload.single("image"), AuthenticateMiddleware, SuperAdminCheck, controller.createUser);
|
||||
router.post("/:id", upload.single("image"), AuthenticateMiddleware, SuperAdminCheck, controller.updateUser);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const UserPermission = require("./../models/UserPermission");
|
||||
const { InventoryScopes, WarehouseScopes } = require("./../config/constants");
|
||||
const { UserActions, InventoryScopes, WarehouseScopes } = require("./../config/constants");
|
||||
|
||||
const getScopes = async (scopes, searchSet) => {
|
||||
const verifiedScopes = [];
|
||||
@@ -93,4 +93,11 @@ module.exports = {
|
||||
next(e);
|
||||
}
|
||||
},
|
||||
getAllActions: async (req, res, next) => {
|
||||
try {
|
||||
res.send({ success: true, data: UserActions });
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,4 +7,6 @@ router.post("/create", controller.createPermission);
|
||||
router.post("/:id", controller.updatePermission);
|
||||
router.delete("/:id", controller.deletePermission);
|
||||
|
||||
router.get("/actions/all", controller.getAllActions);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const mongoose = require("mongoose");
|
||||
const Item = require("../../models/Item");
|
||||
const Warehouse = require("./../../models/Warehouse");
|
||||
|
||||
const lookupPipeline = (from, localField, foreignField, as) => {
|
||||
@@ -74,9 +75,167 @@ const sublevelFilterAggregationPipeline = (warehouse, zone, area, row) => {
|
||||
return sublevelFilterAggregationPipeline;
|
||||
};
|
||||
|
||||
const itemAssociationFilterAggregationPipeline = (inventory, widgetFamily, page, perPage, showAllItems) => {
|
||||
const itemFilterAggregationPipeline = [];
|
||||
itemFilterAggregationPipeline.push(lookupPipeline("widgetfamilies", "widgetFamily", "_id", "widgetfamily"));
|
||||
itemFilterAggregationPipeline.push(unwindPipeline("widgetfamily"));
|
||||
const matchQuery = {};
|
||||
if (inventory) {
|
||||
matchQuery["widgetfamily.inventory"] = mongoose.Types.ObjectId(inventory);
|
||||
}
|
||||
if (widgetFamily) {
|
||||
matchQuery["widgetfamily._id"] = mongoose.Types.ObjectId(widgetFamily);
|
||||
}
|
||||
if (Object.values(matchQuery).length > 0) {
|
||||
itemFilterAggregationPipeline.push({
|
||||
$match: matchQuery,
|
||||
});
|
||||
}
|
||||
itemFilterAggregationPipeline.push(lookupPipeline("inventories", "widgetfamily.inventory", "_id", "inventory"));
|
||||
itemFilterAggregationPipeline.push(unwindPipeline("inventory"));
|
||||
itemFilterAggregationPipeline.push(lookupPipeline("itemassociations", "_id", "item_id", "association"));
|
||||
itemFilterAggregationPipeline.push(unwindPipeline("association", false));
|
||||
itemFilterAggregationPipeline.push(lookupPipeline("sublevels", "association.sub_level_id", "_id", "location"));
|
||||
itemFilterAggregationPipeline.push(unwindPipeline("location"));
|
||||
itemFilterAggregationPipeline.push({
|
||||
$project: {
|
||||
_id: 1,
|
||||
commonName: 1,
|
||||
formalName: 1,
|
||||
description: 1,
|
||||
manufacturer: 1,
|
||||
size: 1,
|
||||
color: 1,
|
||||
type: 1,
|
||||
unitOfMaterial: 1,
|
||||
unitCost: 1,
|
||||
packageCount: 1,
|
||||
countPerPallet: 1,
|
||||
countPerPalletPackage: 1,
|
||||
customAttributes: 1,
|
||||
policiesMetadata: 1,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
images: 1,
|
||||
widgetfamily: 1,
|
||||
inventory: 1,
|
||||
location: 1,
|
||||
totalQuantity: "$association.totalQuantity",
|
||||
reservedQuantity: "$association.reservedQuantity",
|
||||
availableQuantity: "$association.availableQuantity",
|
||||
},
|
||||
});
|
||||
itemFilterAggregationPipeline.push({
|
||||
$facet: {
|
||||
result: [
|
||||
{
|
||||
$skip: page * perPage,
|
||||
},
|
||||
{
|
||||
$limit: perPage,
|
||||
},
|
||||
],
|
||||
count: [
|
||||
{
|
||||
$count: "Total",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
itemFilterAggregationPipeline.push({
|
||||
$project: {
|
||||
result: 1,
|
||||
count: {
|
||||
$first: "$count.Total",
|
||||
},
|
||||
},
|
||||
});
|
||||
return itemFilterAggregationPipeline;
|
||||
};
|
||||
|
||||
const itemFilterAggregationPipeline = (inventory, widgetFamily, page, perPage, showAllItems) => {
|
||||
const itemFilterAggregationPipeline = [];
|
||||
itemFilterAggregationPipeline.push(lookupPipeline("widgetfamilies", "widgetFamily", "_id", "widgetfamily"));
|
||||
itemFilterAggregationPipeline.push(unwindPipeline("widgetfamily"));
|
||||
const matchQuery = {};
|
||||
if (inventory) {
|
||||
matchQuery["widgetfamily.inventory"] = mongoose.Types.ObjectId(inventory);
|
||||
}
|
||||
if (widgetFamily) {
|
||||
matchQuery["widgetfamily._id"] = mongoose.Types.ObjectId(widgetFamily);
|
||||
}
|
||||
if (Object.values(matchQuery).length > 0) {
|
||||
itemFilterAggregationPipeline.push({
|
||||
$match: matchQuery,
|
||||
});
|
||||
}
|
||||
itemFilterAggregationPipeline.push(lookupPipeline("inventories", "widgetfamily.inventory", "_id", "inventory"));
|
||||
itemFilterAggregationPipeline.push(unwindPipeline("inventory"));
|
||||
itemFilterAggregationPipeline.push({
|
||||
$project: {
|
||||
_id: 1,
|
||||
commonName: 1,
|
||||
formalName: 1,
|
||||
description: 1,
|
||||
manufacturer: 1,
|
||||
size: 1,
|
||||
color: 1,
|
||||
type: 1,
|
||||
unitOfMaterial: 1,
|
||||
unitCost: 1,
|
||||
packageCount: 1,
|
||||
countPerPallet: 1,
|
||||
countPerPalletPackage: 1,
|
||||
customAttributes: 1,
|
||||
policiesMetadata: 1,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
images: 1,
|
||||
widgetfamily: 1,
|
||||
inventory: 1,
|
||||
},
|
||||
});
|
||||
itemFilterAggregationPipeline.push({
|
||||
$facet: {
|
||||
result: [
|
||||
{
|
||||
$skip: page * perPage,
|
||||
},
|
||||
{
|
||||
$limit: perPage,
|
||||
},
|
||||
],
|
||||
count: [
|
||||
{
|
||||
$count: "Total",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
itemFilterAggregationPipeline.push({
|
||||
$project: {
|
||||
result: 1,
|
||||
count: {
|
||||
$first: "$count.Total",
|
||||
},
|
||||
},
|
||||
});
|
||||
return itemFilterAggregationPipeline;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
filterSublevels: async ({ warehouse, zone, area, row }) => {
|
||||
const pipeline = sublevelFilterAggregationPipeline(warehouse, zone, area, row);
|
||||
return await Warehouse.aggregate(pipeline);
|
||||
},
|
||||
|
||||
filterItems: async (inventory, widgetFamily, page, perPage, showAllItems) => {
|
||||
const pipeline = itemFilterAggregationPipeline(inventory, widgetFamily, page, perPage);
|
||||
return await Item.aggregate(pipeline);
|
||||
},
|
||||
|
||||
filterItemAssociations: async (inventory, widgetFamily, page, perPage, showAllItems) => {
|
||||
const pipeline = itemAssociationFilterAggregationPipeline(inventory, widgetFamily, page, perPage, showAllItems);
|
||||
return await Item.aggregate(pipeline);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ const constants = require("../../config/constants");
|
||||
const authenticate = async (token) => {
|
||||
const decodedToken = jwt.verify(token, JWT_SECRET);
|
||||
if (decodedToken) {
|
||||
return await User.findById(decodedToken.id).populate({ path: "roles", populate: "permissions" }).populate("permissions");
|
||||
return await User.findById(decodedToken.id).populate("roles");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const Warehouse = require("../models/Warehouse");
|
||||
const mongoose = require("mongoose");
|
||||
const { S3 } = require("./../config/aws");
|
||||
const Inventory = require("../models/Inventory");
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
@@ -20,6 +21,9 @@ module.exports = {
|
||||
res.status(404).send({ success: false, message: "not found" });
|
||||
return;
|
||||
}
|
||||
if (warehouseData.image_url) {
|
||||
warehouseData.image_url = S3.generatePresignedUrl(warehouseData.image_url);
|
||||
}
|
||||
res.send({ success: true, data: warehouseData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -30,19 +34,24 @@ module.exports = {
|
||||
* Create a warehouse
|
||||
*/
|
||||
createWarehouse: async (req, res, next) => {
|
||||
const { name, address, specs, company_id } = req.body;
|
||||
const { name, address, specs, company_id, preferredInventories } = req.body;
|
||||
|
||||
if (!(name && address)) {
|
||||
res.status(400).send("Missing params param");
|
||||
return;
|
||||
}
|
||||
|
||||
let preferredInventoryObjects;
|
||||
if (preferredInventories) {
|
||||
preferredInventoryObjects = await Inventory.find({ _id: { $in: preferredInventories } });
|
||||
}
|
||||
try {
|
||||
const warehouseData = new Warehouse({
|
||||
name,
|
||||
address,
|
||||
specs,
|
||||
company_id: mongoose.Types.ObjectId(company_id),
|
||||
preferredInventories: preferredInventoryObjects,
|
||||
});
|
||||
|
||||
await warehouseData.save();
|
||||
@@ -51,45 +60,21 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const images = req.files;
|
||||
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
const url = await S3.uploadFile(
|
||||
`warehouse/${warehouseData._id.toString()}-${Date.now()}-${i}.${images[i].originalname.split(".").slice(-1).pop()}`,
|
||||
images[i].path
|
||||
);
|
||||
warehouseData.images ||= [];
|
||||
warehouseData.images.push({ url });
|
||||
const image = req.file;
|
||||
if (image) {
|
||||
const url = await S3.uploadFile(`warehouse/${warehouseData._id.toString()}.${image.originalname.split(".").slice(-1).pop()}`, image.path);
|
||||
warehouseData.image_url = url;
|
||||
await warehouseData.save();
|
||||
}
|
||||
if (warehouseData.image_url) {
|
||||
warehouseData.image_url = S3.generatePresignedUrl(warehouseData.image_url);
|
||||
}
|
||||
warehouseData.save();
|
||||
res.send({ success: true, message: warehouseData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload an image for the warehouse
|
||||
*/
|
||||
addWarehouseImage: async (req, res, next) => {
|
||||
console.dir("Warehouse image uploaded:", { file: req.file });
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const warehouseDetails = await Warehouse.findById(id);
|
||||
if (!warehouseDetails) {
|
||||
res.send({ success: false, message: "ID not found" });
|
||||
return;
|
||||
}
|
||||
warehouseDetails.imageUrl = req.file.path;
|
||||
await warehouseDetails.save();
|
||||
res.send({ success: true, data: warehouseDetails });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update a warehouses detail
|
||||
*/
|
||||
@@ -101,9 +86,9 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, address, specs, company_id } = req.body;
|
||||
|
||||
if (!(name || address || specs || company_id)) {
|
||||
const { name, address, specs, company_id, preferredInventories } = req.body;
|
||||
const image = req.file;
|
||||
if (!(name || address || specs || company_id || image)) {
|
||||
res.status(400).send({ success: false, message: "Missing data in body" });
|
||||
return;
|
||||
}
|
||||
@@ -119,8 +104,18 @@ module.exports = {
|
||||
if (address) warehouseData.address = address;
|
||||
if (specs) warehouseData.specs = specs;
|
||||
if (company_id) warehouseData.company_id = mongoose.Types.ObjectId(company_id);
|
||||
|
||||
if (image) {
|
||||
const url = await S3.uploadFile(`warehouseData/${warehouseData._id.toString()}.${image.originalname.split(".").slice(-1).pop()}`, image.path);
|
||||
warehouseData.image_url = url;
|
||||
}
|
||||
if (preferredInventories) {
|
||||
const preferredInventoryObjects = await Inventory.find({ _id: { $in: preferredInventories } });
|
||||
warehouseData.preferredInventories = preferredInventoryObjects;
|
||||
}
|
||||
await warehouseData.save();
|
||||
if (warehouseData.image_url) {
|
||||
warehouseData.image_url = S3.generatePresignedUrl(warehouseData.image_url);
|
||||
}
|
||||
res.send({ success: true, data: warehouseData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -132,6 +127,9 @@ module.exports = {
|
||||
const { getAllWithPagination } = require("./utils/pagination");
|
||||
const { page, perPage } = req.query;
|
||||
const data = await getAllWithPagination(Warehouse, page, perPage);
|
||||
for (const warehouse of data) {
|
||||
if (warehouse.image_url) warehouse.image_url = S3.generatePresignedUrl(warehouse.image_url);
|
||||
}
|
||||
res.send({ success: true, data: data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -173,7 +171,7 @@ module.exports = {
|
||||
`warehouse/${warehouse._id.toString()}-${Date.now()}-0.${image.originalname.split(".").slice(-1).pop()}`,
|
||||
image.path
|
||||
);
|
||||
warehouse.images ||= [];
|
||||
warehouse.images = warehouse.images || [];
|
||||
warehouse.images.push({ url });
|
||||
await warehouse.save();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const router = require("express").Router();
|
||||
const upload = require("../middleware/fileUpload");
|
||||
const controller = require("./warehouse.controller");
|
||||
const multer = require("multer");
|
||||
const upload = multer({ dest: "tmp/uploads/" });
|
||||
|
||||
/**
|
||||
* @route /warehouse/
|
||||
@@ -20,27 +21,11 @@ router.get("/:id/zones", controller.getWarehouseZonesByID);
|
||||
/**
|
||||
* @route /warehouse/
|
||||
*/
|
||||
router.post("/", upload.any("images"), controller.createWarehouse);
|
||||
|
||||
/**
|
||||
* @route /warehouse/:id/image
|
||||
*/
|
||||
router.post("/:id/image", upload.single("image"), controller.addImageToWarehouse);
|
||||
|
||||
/**
|
||||
* @route /warehouse/:id/image/:image_id
|
||||
*/
|
||||
router.delete("/:id/image/:image_id", controller.removeImageFromWarehouse);
|
||||
|
||||
|
||||
/**
|
||||
* @route /warehouse/add-image
|
||||
*/
|
||||
router.post("/add-image/:id", upload.single("warehouse-image"), controller.addWarehouseImage);
|
||||
router.post("/", upload.single("image"), controller.createWarehouse);
|
||||
|
||||
/**
|
||||
* @route /warehouse/
|
||||
*/
|
||||
router.patch("/:id", controller.updateWarehouseByID);
|
||||
router.patch("/:id", upload.single("image"), controller.updateWarehouseByID);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -96,21 +96,21 @@ module.exports = {
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id) {
|
||||
res.status(400).send("Missing id param");
|
||||
res.status(400).send({ success: false, error: "Missing id param" });
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, parentId, inventoryId } = req.body;
|
||||
|
||||
if (!(name || parentId || inventoryId)) {
|
||||
res.status(400).send("Missing data in body");
|
||||
res.status(400).send({ success: false, error: "Missing data in body" });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const widgetFamilyData = await WidgetFamily.findById(id);
|
||||
if (!widgetFamilyData) {
|
||||
res.status(404);
|
||||
res.status(404).send({ success: false, error: "Widget not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,8 +122,10 @@ module.exports = {
|
||||
if (parentId && mongoose.isValidObjectId(parentId)) {
|
||||
parent = await WidgetFamily.findById(parentId);
|
||||
widgetFamilyData.parent = parent;
|
||||
} else if (parentId && parentId == "NA") {
|
||||
widgetFamilyData.parent = undefined;
|
||||
} else if (parentId && !mongoose.isValidObjectId(parentId)) {
|
||||
res.status(400).send("Invalid params parentId");
|
||||
res.status(400).send({ success: false, error: "Invalid params parentId" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,7 +134,7 @@ module.exports = {
|
||||
inventory = await Inventory.findById(inventoryId);
|
||||
widgetFamilyData.inventory = inventory;
|
||||
} else if (inventoryId && !mongoose.isValidObjectId(inventoryId)) {
|
||||
res.status(400).send("Invalid params inventoryId");
|
||||
res.status(400).send({ success: false, error: "Invalid params inventoryId" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,6 +144,21 @@ module.exports = {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
deleteWidgetFamilyByID: async (req, res, next) => {
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id || !mongoose.isValidObjectId(id)) {
|
||||
res.status(400).send({ success: false, error: "Missing/invalid id param" });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await WidgetFamily.deleteOne({ _id: id });
|
||||
res.send({ success: true });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Gets the WidgetFamily data by `inventory`
|
||||
*/
|
||||
|
||||
@@ -26,4 +26,9 @@ router.get("/:id", controller.getWidgetFamilyByID);
|
||||
*/
|
||||
router.get("/:id/children", controller.getWidgetFamilyChildrenByID);
|
||||
|
||||
/**
|
||||
* @route /widgetFamily/
|
||||
*/
|
||||
router.delete("/:id", controller.deleteWidgetFamilyByID);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
20
src/models/BaseSchema.js
Normal file
20
src/models/BaseSchema.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const mongoose = require("mongoose");
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
// eslint-disable-next-line require-jsdoc
|
||||
const BaseSchema = new Schema(
|
||||
{
|
||||
createdBy: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
},
|
||||
updatedBy: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
},
|
||||
createdAt: Date,
|
||||
updatedAt: Date
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = BaseSchema;
|
||||
@@ -1,5 +1,4 @@
|
||||
const mongoose = require("mongoose");
|
||||
// const { InventoryTypes } = require("./../config/constants");
|
||||
|
||||
const schema = new mongoose.Schema(
|
||||
{
|
||||
@@ -8,10 +7,6 @@ const schema = new mongoose.Schema(
|
||||
required: true,
|
||||
trim: true,
|
||||
},
|
||||
image_url: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
icon_slug: {
|
||||
type: String,
|
||||
trim: true,
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
const mongoose = require("mongoose");
|
||||
const BaseSchema = require("./BaseSchema");
|
||||
const { isEmail } = require("validator");
|
||||
const bcrypt = require("bcrypt");
|
||||
const { UserActions, WarehouseScopes, InventoryScopes, AllUIModules } = require("./../config/constants");
|
||||
|
||||
const schema = new mongoose.Schema(
|
||||
{
|
||||
...BaseSchema.obj,
|
||||
fullName: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
phoneNumber: {
|
||||
type: String,
|
||||
trim: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: [true, "Please enter an email"],
|
||||
@@ -37,6 +43,10 @@ const schema = new mongoose.Schema(
|
||||
passwordResetToken: {
|
||||
type: String,
|
||||
},
|
||||
image_url: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
roles: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
@@ -82,13 +92,11 @@ const schema = new mongoose.Schema(
|
||||
},
|
||||
],
|
||||
},
|
||||
createdBy: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
lastActive: Date
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -23,7 +23,10 @@ const schema = new mongoose.Schema(
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
images: [WarehouseImage],
|
||||
image_url: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
company_id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Company",
|
||||
@@ -34,6 +37,12 @@ const schema = new mongoose.Schema(
|
||||
ref: "Zone",
|
||||
},
|
||||
],
|
||||
preferredInventories: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Inventory",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
Reference in New Issue
Block a user