This commit is contained in:
m0n02hz
2022-03-03 04:15:45 +05:30
20 changed files with 472 additions and 195 deletions

21
package-lock.json generated
View File

@@ -5,7 +5,6 @@
"requires": true,
"packages": {
"": {
"name": "plaidware-wms-core",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
@@ -2370,14 +2369,22 @@
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
},
"node_modules/node-fetch": {
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/node-fetch/node_modules/tr46": {
@@ -5395,9 +5402,9 @@
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
},
"node-fetch": {
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
},

View File

@@ -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,

View File

@@ -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);
}
}
};

View File

@@ -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
*/

View File

@@ -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 });
},
};

View File

@@ -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
*/

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}
}
};

View File

@@ -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;

View File

@@ -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);
},
};

View File

@@ -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");
}
};

View File

@@ -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();

View File

@@ -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;

View File

@@ -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`
*/

View File

@@ -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
View 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;

View File

@@ -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,

View File

@@ -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
}
);

View File

@@ -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,