Merge pull request #10 from kfnawaz/Feat-sublevel-crud
Feat-sublevel crud
This commit is contained in:
@@ -12,6 +12,17 @@ const UserActions = [
|
|||||||
"Receive",
|
"Receive",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const LevelPositions = [
|
||||||
|
"LDB",
|
||||||
|
"LDF",
|
||||||
|
"LUB",
|
||||||
|
"LUF",
|
||||||
|
"RDB",
|
||||||
|
"RDF",
|
||||||
|
"RUB",
|
||||||
|
"RUF",
|
||||||
|
];
|
||||||
|
|
||||||
const InventoryScopes = ["Inventory", "Material", "Item"];
|
const InventoryScopes = ["Inventory", "Material", "Item"];
|
||||||
|
|
||||||
const WarehouseScopes = [
|
const WarehouseScopes = [
|
||||||
@@ -40,6 +51,8 @@ const CustomAttributeTypes = [
|
|||||||
"Enumerable",
|
"Enumerable",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const SublevelInventoryTypes = ["Inventory", "Material", "Item"];
|
||||||
|
|
||||||
const AUTHENTICATION_FAILURE_ERROR_MESSAGE = "Authentication Failed!";
|
const AUTHENTICATION_FAILURE_ERROR_MESSAGE = "Authentication Failed!";
|
||||||
const AUTHORIZATION_FAILURE_ERROR_MESSAGE =
|
const AUTHORIZATION_FAILURE_ERROR_MESSAGE =
|
||||||
"User not permitted due to lack of access!";
|
"User not permitted due to lack of access!";
|
||||||
@@ -52,7 +65,9 @@ module.exports = {
|
|||||||
WarehouseScopes,
|
WarehouseScopes,
|
||||||
InventoryTypes,
|
InventoryTypes,
|
||||||
CustomAttributeTypes,
|
CustomAttributeTypes,
|
||||||
|
SublevelInventoryTypes,
|
||||||
SubLevelTypes,
|
SubLevelTypes,
|
||||||
|
LevelPositions,
|
||||||
SUPER_ADMIN_ROLE: "super-admin",
|
SUPER_ADMIN_ROLE: "super-admin",
|
||||||
COMPANY_ADMIN_ROLE: "company-admin",
|
COMPANY_ADMIN_ROLE: "company-admin",
|
||||||
WAREHOUSE_ADMIN_ROLE: "warehouse-admin",
|
WAREHOUSE_ADMIN_ROLE: "warehouse-admin",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const areaRouter = require("./area.router");
|
|||||||
const bayRouter = require("./bay.router");
|
const bayRouter = require("./bay.router");
|
||||||
const rowRouter = require("./row.router");
|
const rowRouter = require("./row.router");
|
||||||
const levelRouter = require("./level.router");
|
const levelRouter = require("./level.router");
|
||||||
|
const sublevelRouter = require("./sublevel.router");
|
||||||
const dashboardRouter = require("./dashboard.router");
|
const dashboardRouter = require("./dashboard.router");
|
||||||
|
|
||||||
router.use("/user-role", AuthenticateMiddleware, userRoleRouter);
|
router.use("/user-role", AuthenticateMiddleware, userRoleRouter);
|
||||||
@@ -23,6 +24,7 @@ router.use("/area", areaRouter);
|
|||||||
router.use("/bay", bayRouter);
|
router.use("/bay", bayRouter);
|
||||||
router.use("/row", rowRouter);
|
router.use("/row", rowRouter);
|
||||||
router.use("/level", levelRouter);
|
router.use("/level", levelRouter);
|
||||||
|
router.use("/sublevel", sublevelRouter);
|
||||||
router.use("/dashboard", dashboardRouter);
|
router.use("/dashboard", dashboardRouter);
|
||||||
|
|
||||||
router.get("/", (req, res) => {
|
router.get("/", (req, res) => {
|
||||||
|
|||||||
154
src/controller/sublevel.controller.js
Normal file
154
src/controller/sublevel.controller.js
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
const Sublevel = require("../models/Sublevel");
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
const { addSublevelToParent, deleteSubLevelTreeFromRoot, validPositions } = require("./utils/sublevel");
|
||||||
|
const { SubLevelTypes } = require("../config/constants");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Gets the sublevel data by `id`
|
||||||
|
*/
|
||||||
|
getSubLevelByID: async (req, res, next) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
res.status(400).send("Missing id param");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const subLevelData = await Sublevel.findById(id);
|
||||||
|
if (!subLevelData) {
|
||||||
|
res.status(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.send(subLevelData);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a sublevel
|
||||||
|
*/
|
||||||
|
createSubLevel: async (req, res, next) => {
|
||||||
|
const { name, type, specs, parent_id, parentIsLevel, positions } = req.body;
|
||||||
|
|
||||||
|
if (!(name && type && parent_id && positions)) {
|
||||||
|
res.status(400).send("Missing params param");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SubLevelTypes.includes(type)) {
|
||||||
|
res.status(400).send("Invalid type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validPositions(positions)) {
|
||||||
|
res.status(400).send("Invalid positions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parentData = parentIsLevel ? { parent_current_depth: 0, parent_main_level_id: parent_id } : await Sublevel.findById(parent_id);
|
||||||
|
|
||||||
|
const { parent_current_depth, parent_main_level_id } = parentData;
|
||||||
|
|
||||||
|
const sublevelData = new Sublevel({
|
||||||
|
name,
|
||||||
|
type: type,
|
||||||
|
specs,
|
||||||
|
main_level_id: parent_main_level_id,
|
||||||
|
current_depth: parent_current_depth + 1,
|
||||||
|
parent_sublevel_id: mongoose.Types.ObjectId(parent_id),
|
||||||
|
preffered_inventory: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await addSublevelToParent({ type, positions, sub_level_id: sublevelData._id.toString() }, parent_id, parentIsLevel);
|
||||||
|
|
||||||
|
await sublevelData.save();
|
||||||
|
if (!sublevelData) {
|
||||||
|
res.status(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.send(sublevelData);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a sublevels' detail
|
||||||
|
*/
|
||||||
|
updateSubLevelDetailsByID: async (req, res, next) => {
|
||||||
|
const { name, type, specs } = req.body;
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
if (!(name || type || specs)) {
|
||||||
|
res.status(400).send("Missing params param");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type && !SubLevelTypes.includes(type)) {
|
||||||
|
res.status(400).send("Invalid type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sublevelData = await Sublevel.findById(id);
|
||||||
|
if (!sublevelData) {
|
||||||
|
res.status(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name) sublevelData.name = name;
|
||||||
|
if (type) sublevelData.type = type;
|
||||||
|
if (specs) sublevelData.specs = specs;
|
||||||
|
|
||||||
|
const newData = await sublevelData.save();
|
||||||
|
req.send(newData);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a sublevel
|
||||||
|
*/
|
||||||
|
deleteSublevel: async (req, res, next) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
if (!id) {
|
||||||
|
res.status(404).send("Provide an ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const deletedSublevels = deleteSubLevelTreeFromRoot(id);
|
||||||
|
res.send({ success: deletedSublevels.length, data: { deletedSublevels: deletedSublevels } });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add preffered_inventory to a sublevel
|
||||||
|
*/
|
||||||
|
addInventory: async (req, res, next) => {
|
||||||
|
const { id, inventory } = req.body;
|
||||||
|
const sublevelData = await Sublevel.findById(id);
|
||||||
|
if (!sublevelData) {
|
||||||
|
res.status(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
|
if (inventory.every((_) => _.hasOwnProperty("id") && _.hasOwnProperty("type"))) {
|
||||||
|
res.status(404).send({ success: false, message: "invalid inventory data" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
sublevelData.preffered_inventory.push(...inventory);
|
||||||
|
await sublevelData.save();
|
||||||
|
res.send({ success: true, data: sublevelData.preffered_inventory });
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
29
src/controller/sublevel.router.js
Normal file
29
src/controller/sublevel.router.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const router = require("express").Router();
|
||||||
|
const controller = require("./sublevel.controller");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route /sublevel/:id
|
||||||
|
*/
|
||||||
|
router.get("/:id", controller.getSubLevelByID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route /sublevel/
|
||||||
|
*/
|
||||||
|
router.post("/", controller.createSubLevel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route /sublevel/:id
|
||||||
|
*/
|
||||||
|
router.patch("/:id", controller.updateSubLevelDetailsByID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route /sublevel/:id
|
||||||
|
*/
|
||||||
|
router.delete("/:id", controller.deleteSublevel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route /sublevel/add-inventory
|
||||||
|
*/
|
||||||
|
router.post("/add-inventory", controller.addInventory);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
114
src/controller/utils/sublevel.js
Normal file
114
src/controller/utils/sublevel.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
const Sublevel = require("../../models/Sublevel");
|
||||||
|
const Level = require("../../models/Level");
|
||||||
|
const { LevelPositions } = require("../../config/constants");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To move a sub level
|
||||||
|
* @param {string} sub_level_id The Sublevel to me moved
|
||||||
|
* @param {string} parent_sub_or_level_id The Level/Sublevel under which to be moved
|
||||||
|
* @returns {{success: boolean, message: string}}
|
||||||
|
*/
|
||||||
|
const moveSublevel = async (sub_level_id, parent_sub_or_level_id) => {
|
||||||
|
/**
|
||||||
|
* - Check if depths of parent_sub_level_id and parent_sub_or_level_id are same
|
||||||
|
* - Copy and add references
|
||||||
|
* - Delete and remove references
|
||||||
|
*/
|
||||||
|
return { success: false, message: "not implemented" };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To delete a sub level and all corresponding sub levels under it
|
||||||
|
* @param {string} root_sub_level_id The root Sublevel ID
|
||||||
|
* @returns {string[]} The Sublevel IDs that have been deleted
|
||||||
|
*/
|
||||||
|
const deleteSubLevelTreeFromRoot = async (root_sub_level_id) => {
|
||||||
|
let sub_level_ids = [];
|
||||||
|
let temp_sub_level_ids = [root_sub_level_id];
|
||||||
|
|
||||||
|
// remove from parent first
|
||||||
|
await removeSublevelFromParent(root_sub_level_id);
|
||||||
|
|
||||||
|
while (temp_sub_level_ids.length > 0) {
|
||||||
|
const level_sub_level_data = await Sublevel.find({
|
||||||
|
_id: temp_sub_level_ids,
|
||||||
|
});
|
||||||
|
|
||||||
|
sub_level_ids = [...sub_level_ids, ...temp_sub_level_ids];
|
||||||
|
temp_sub_level_ids = [];
|
||||||
|
|
||||||
|
level_sub_level_data.forEach((sub_level_data) => {
|
||||||
|
sub_level_data.sub_levels.forEach((sub_level) => {
|
||||||
|
temp_sub_level_ids.push(sub_level.sub_level_id.toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await Sublevel.deleteMany({ _id: sub_level_ids });
|
||||||
|
console.log("Deleting sub-level tree", { sub_level_ids });
|
||||||
|
return sub_level_ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the sublevel data to the parent document
|
||||||
|
* @param {{type: string, positions: string[], sub_level_id: string}} payload The sublevel data
|
||||||
|
* @param {string} parent_id The parent level ID
|
||||||
|
* @param {boolean} parentIsLevel Is parent a level?
|
||||||
|
*/
|
||||||
|
const addSublevelToParent = async (payload, parent_id, parentIsLevel) => {
|
||||||
|
if (parentIsLevel) {
|
||||||
|
// add sublevel to parent
|
||||||
|
const parentData = await Sublevel.findById(parent_id);
|
||||||
|
parentData.sub_levels.push(payload);
|
||||||
|
return await parentData.save();
|
||||||
|
} else {
|
||||||
|
// add sublevel to sublevel
|
||||||
|
const parentData = await Sublevel.findById(parent_id);
|
||||||
|
parentData.sub_levels.push(payload);
|
||||||
|
return await parentData.save();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeSublevelFromParent = async (id) => {
|
||||||
|
const { main_level_id, parent_sublevel_id, current_depth } = await Sublevel.findById(id);
|
||||||
|
if (current_depth == 1) {
|
||||||
|
// it means parent is level
|
||||||
|
const parentData = await Level.findById(main_level_id);
|
||||||
|
parentData.sub_levels = parentData.sub_levels.filter((sub_level) => sub_level.sub_level_id != id);
|
||||||
|
} else {
|
||||||
|
// parent is another sublevel
|
||||||
|
const parentData = await Sublevel.findById(parent_sublevel_id);
|
||||||
|
parentData.sub_levels = parentData.sub_levels.filter((sub_level) => sub_level.sub_level_id != id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a list of available positions at the particular Level/Sublevel
|
||||||
|
* @param {object} sublevelData Level / Sublevel mongoose document
|
||||||
|
* @returns {string[]} The list of available positions
|
||||||
|
*/
|
||||||
|
const findAvailablePositions = (sublevelData) => {
|
||||||
|
let positionsOccupied = [];
|
||||||
|
sublevelData.sub_levels.forEach((sublevel) => {
|
||||||
|
positionsOccupied = [...positionsOccupied, ...sublevel.sub_levels];
|
||||||
|
});
|
||||||
|
return LevelPositions.filter((pos) => !positionsOccupied.includes(pos));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if positions are valid positions
|
||||||
|
* @param {string[]} positions An array of positions
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const validPositions = (positions) => {
|
||||||
|
return positions.every((position) => LevelPositions.includes(position));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
addSublevelToParent,
|
||||||
|
removeSublevelFromParent,
|
||||||
|
deleteSubLevelTreeFromRoot,
|
||||||
|
moveSublevel,
|
||||||
|
findAvailablePositions,
|
||||||
|
validPositions,
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
const { SubLevelTypes } = require("../config/constants");
|
const { SubLevelTypes, LevelPositions } = require("../config/constants");
|
||||||
|
|
||||||
const schema = new mongoose.Schema(
|
const schema = new mongoose.Schema(
|
||||||
{
|
{
|
||||||
@@ -14,8 +14,7 @@ const schema = new mongoose.Schema(
|
|||||||
trim: true,
|
trim: true,
|
||||||
},
|
},
|
||||||
specs: {
|
specs: {
|
||||||
// TODO: TBD
|
type: Object,
|
||||||
type: String,
|
|
||||||
trim: true,
|
trim: true,
|
||||||
},
|
},
|
||||||
bay_id: {
|
bay_id: {
|
||||||
@@ -27,18 +26,14 @@ const schema = new mongoose.Schema(
|
|||||||
type: {
|
type: {
|
||||||
required: true,
|
required: true,
|
||||||
type: String,
|
type: String,
|
||||||
enum: SubLevelTypes
|
enum: SubLevelTypes,
|
||||||
},
|
|
||||||
depth: {
|
|
||||||
required: true,
|
|
||||||
type: Number,
|
|
||||||
min: 1, // Level is at 0
|
|
||||||
max: 5,
|
|
||||||
},
|
|
||||||
bay_id: {
|
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
|
||||||
ref: "Bay",
|
|
||||||
},
|
},
|
||||||
|
postitions: [
|
||||||
|
{
|
||||||
|
type: String,
|
||||||
|
enum: LevelPositions,
|
||||||
|
},
|
||||||
|
],
|
||||||
sub_level_id: {
|
sub_level_id: {
|
||||||
required: true,
|
required: true,
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
const { SubLevelTypes } = require("../config/constants");
|
const { SubLevelTypes, LevelPositions, SublevelInventoryTypes } = require("../config/constants");
|
||||||
|
|
||||||
const schema = new mongoose.Schema(
|
const schema = new mongoose.Schema(
|
||||||
{
|
{
|
||||||
@@ -11,11 +11,10 @@ const schema = new mongoose.Schema(
|
|||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
enum: SubLevelTypes
|
enum: SubLevelTypes,
|
||||||
},
|
},
|
||||||
specs: {
|
specs: {
|
||||||
// TBD
|
type: Object,
|
||||||
type: String,
|
|
||||||
trim: true,
|
trim: true,
|
||||||
},
|
},
|
||||||
main_level_id: {
|
main_level_id: {
|
||||||
@@ -39,12 +38,12 @@ const schema = new mongoose.Schema(
|
|||||||
type: String,
|
type: String,
|
||||||
enum: SubLevelTypes,
|
enum: SubLevelTypes,
|
||||||
},
|
},
|
||||||
depth: {
|
postitions: [
|
||||||
required: true,
|
{
|
||||||
type: Number,
|
type: String,
|
||||||
min: 1, // Level is at 0
|
enum: LevelPositions,
|
||||||
max: 5,
|
},
|
||||||
},
|
],
|
||||||
sub_level_id: {
|
sub_level_id: {
|
||||||
required: true,
|
required: true,
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
@@ -56,10 +55,16 @@ const schema = new mongoose.Schema(
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
inventory: [
|
preffered_inventory: [
|
||||||
{
|
{
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
id: {
|
||||||
ref: "Inventory",
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
refPath: "type",
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
enum: SublevelInventoryTypes,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user