From abf2899edea2b4856d7d64540e317e83b9ec7e8c Mon Sep 17 00:00:00 2001 From: Sathishkumar Krishnan Date: Fri, 7 Jan 2022 08:07:46 +0530 Subject: [PATCH 1/5] feat: added skeleton for item transaction apis --- src/controller/item.controller.js | 21 ++++++++++++++++++ src/controller/item.router.js | 37 ++++++++++++++++++++++++++++++- src/controller/utils/authorize.js | 34 +++++++++------------------- 3 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/controller/item.controller.js b/src/controller/item.controller.js index e7e6523..fc5683e 100644 --- a/src/controller/item.controller.js +++ b/src/controller/item.controller.js @@ -194,4 +194,25 @@ module.exports = { next(error); } }, + pickItem: async (req, res, next) => { + res.status(500).send({ success: false, error: "Not Implemented" }); + }, + putItem: async (req, res, next) => { + res.status(500).send({ success: false, error: "Not Implemented" }); + }, + reserveItem: async (req, res, next) => { + res.status(500).send({ success: false, error: "Not Implemented" }); + }, + checkInItem: async (req, res, next) => { + res.status(500).send({ success: false, error: "Not Implemented" }); + }, + checkOutItem: async (req, res, next) => { + res.status(500).send({ success: false, error: "Not Implemented" }); + }, + reportItem: async (req, res, next) => { + res.status(500).send({ success: false, error: "Not Implemented" }); + }, + adjustItem: async (req, res, next) => { + res.status(500).send({ success: false, error: "Not Implemented" }); + }, }; diff --git a/src/controller/item.router.js b/src/controller/item.router.js index ec27913..174f0b1 100644 --- a/src/controller/item.router.js +++ b/src/controller/item.router.js @@ -1,6 +1,6 @@ const router = require("express").Router(); const controller = require("./item.controller"); - +const { ItemTransactionCheck } = require("./utils/authorize"); /** * @route /item/ */ @@ -21,4 +21,39 @@ router.get("/filter", controller.getItemsByFilter); */ router.get("/:id", controller.getItemByID); +/** + * @route /item/:id/pick + */ +router.post("/:id/pick", ItemTransactionCheck, controller.pickItem); + +/** + * @route /item/:id/put + */ +router.post("/:id/put", ItemTransactionCheck, controller.putItem); + +/** + * @route /item/:id/reserve + */ +router.post("/:id/reserve", ItemTransactionCheck, controller.reserveItem); + +/** + * @route /item/:id/check-in + */ +router.post("/:id/check-in", ItemTransactionCheck, controller.checkInItem); + +/** + * @route /item/:id/check-out + */ +router.post("/:id/check-out", ItemTransactionCheck, controller.checkOutItem); + +/** + * @route /item/:id/report + */ +router.post("/:id/report", ItemTransactionCheck, controller.reportItem); + +/** + * @route /item/:id/adjust + */ +router.post("/:id/adjust", ItemTransactionCheck, controller.adjustItem); + module.exports = router; diff --git a/src/controller/utils/authorize.js b/src/controller/utils/authorize.js index 286810b..8c98129 100644 --- a/src/controller/utils/authorize.js +++ b/src/controller/utils/authorize.js @@ -1,38 +1,22 @@ const jwt = require("jsonwebtoken"); const User = require("../../models/User"); const UserRole = require("../../models/UserRole"); -const { - SUPER_ADMIN_ROLE, - AUTHORIZATION_FAILURE_ERROR_MESSAGE, -} = require("../../config/constants"); +const { SUPER_ADMIN_ROLE, AUTHORIZATION_FAILURE_ERROR_MESSAGE } = require("../../config/constants"); const { JWT_SECRET } = require("../../config/env"); 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({ path: "roles", populate: "permissions" }).populate("permissions"); } }; -const authorize = async ( - user, - requiredRoles = [], - requiredPermissions = [] -) => { +const authorize = async (user, requiredRoles = [], requiredPermissions = []) => { const userRoles = user.roles.map((_) => _._id); - const userPermissions = [ - ...user.permissions.map((_) => _._id), - ...userRoles.map((_) => _.permissions).flat(), - ]; + const userPermissions = [...user.permissions.map((_) => _._id), ...userRoles.map((_) => _.permissions).flat()]; - return ( - user != undefined && - requiredRoles.every((_) => userRoles.includes(_)) && - requiredPermissions.every((_) => userPermissions.includes(_)) - ); + return user != undefined && requiredRoles.every((_) => userRoles.includes(_)) && requiredPermissions.every((_) => userPermissions.includes(_)); }; module.exports = { @@ -41,11 +25,13 @@ module.exports = { if (authorize(res.locals.user, [SuperAdmin.id])) { next(); } else { - res - .status(403) - .send({ success: false, error: AUTHORIZATION_FAILURE_ERROR_MESSAGE }); + res.status(403).send({ success: false, error: AUTHORIZATION_FAILURE_ERROR_MESSAGE }); } }, + ItemTransactionCheck: async (req, res, next) => { + // WIP + next(); + }, AuthenticateMiddleware: async (req, res, next) => { try { const token = req.headers.authorization || ""; From 19e868e20327d4df044b89c38273b7d74e86151f Mon Sep 17 00:00:00 2001 From: Sathishkumar Krishnan Date: Fri, 7 Jan 2022 08:42:22 +0530 Subject: [PATCH 2/5] feat: added put, pick & reserve apis --- src/controller/item.controller.js | 120 +++++++++++++++++++++++++++++- src/controller/item.router.js | 16 ++-- src/models/ItemAssociation.js | 10 ++- src/models/ItemTransaction.js | 8 ++ 4 files changed, 142 insertions(+), 12 deletions(-) diff --git a/src/controller/item.controller.js b/src/controller/item.controller.js index fc5683e..d5f2ee5 100644 --- a/src/controller/item.controller.js +++ b/src/controller/item.controller.js @@ -2,6 +2,18 @@ const mongoose = require("mongoose"); const Item = require("../models/Item"); const WidgetFamily = require("../models/WidgetFamily"); const Inventory = require("../models/Inventory"); +const { + PickItemTransaction, + PutItemTransaction, + ReserveItemTransaction, + // CheckInItemTransaction, + // CheckOutItemTransaction, + // ReportItemTransaction, + // AdjustItemTransaction, +} = require("../models/ItemTransaction"); + +const ItemAssociation = require("../models/ItemAssociation"); +const Sublevel = require("../models/Sublevel"); const { InventoryTypes } = require("../config/constants"); module.exports = { @@ -195,13 +207,115 @@ module.exports = { } }, pickItem: async (req, res, next) => { - res.status(500).send({ success: false, error: "Not Implemented" }); + try { + const { id } = req.params; + if (!id || mongoose.isValidObjectId(id)) { + res.status(400).send("Missing/Invalid id param"); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).send("item not found"); + return; + } + + const { putQuantity, subLevel } = req.body; + if (!(putQuantity && putQuantity > 0) || !(subLevel && mongoose.isValidObjectId(subLevel))) { + res.status(400).send("Invalid value for putQuantity/subLevel"); + return; + } + + const subLevelObj = await Sublevel.findById(subLevel); + const itemAssociation = await ItemAssociation.findOne({ item_id: item._id, sub_level_id: subLevelObj._id }); + itemAssociation.totalQuantity = itemAssociation.totalQuantity + putQuantity; + itemAssociation.availableQuantity = itemAssociation.availableQuantity + putQuantity; + await itemAssociation.save(); + + await PickItemTransaction.create({ + type: "PICK", + performedOn: item, + performedBy: res.locals.user, + putQuantity: putQuantity, + subLevel: subLevelObj, + }); + } catch (error) { + next(error); + } }, putItem: async (req, res, next) => { - res.status(500).send({ success: false, error: "Not Implemented" }); + try { + const { id } = req.params; + if (!id || mongoose.isValidObjectId(id)) { + res.status(400).send("Missing/Invalid id param"); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).send("item not found"); + return; + } + + const { pickupQuantity, subLevel } = req.body; + if (!(pickupQuantity && pickupQuantity > 0) || !(subLevel && mongoose.isValidObjectId(subLevel))) { + res.status(400).send("Invalid value for pickupQuantity/subLevel"); + return; + } + + const subLevelObj = await Sublevel.findById(subLevel); + const itemAssociation = await ItemAssociation.findOne({ item_id: item._id, sub_level_id: subLevelObj._id }); + itemAssociation.totalQuantity = itemAssociation.totalQuantity - pickupQuantity; + itemAssociation.availableQuantity = itemAssociation.availableQuantity - pickupQuantity; + await itemAssociation.save(); + + await PutItemTransaction.create({ + type: "PUT", + performedOn: item, + performedBy: res.locals.user, + pickupQuantity: pickupQuantity, + subLevel: subLevelObj, + }); + } catch (error) { + next(error); + } }, reserveItem: async (req, res, next) => { - res.status(500).send({ success: false, error: "Not Implemented" }); + try { + const { id } = req.params; + if (!id || mongoose.isValidObjectId(id)) { + res.status(400).send("Missing/Invalid id param"); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).send("item not found"); + return; + } + + const { reserveQuantity, job, pickupDate } = req.body; + if (!(reserveQuantity && reserveQuantity > 0) || !(job && mongoose.isValidObjectId(job)) || !(pickupDate && Date.parse(pickupDate))) { + res.status(400).send("Invalid value for reserveQuantity/job/pickupDate"); + return; + } + + const itemAssociation = await ItemAssociation.findOne({ item_id: item._id, availableQuantity: { $gte: reserveQuantity } }); + itemAssociation.reservedQuantity = itemAssociation.reservedQuantity + reserveQuantity; + itemAssociation.availableQuantity = itemAssociation.availableQuantity - reserveQuantity; + await itemAssociation.save(); + + await ReserveItemTransaction.create({ + type: "PUT", + performedOn: item, + performedBy: res.locals.user, + reserveQuantity: reserveQuantity, + job: job, + pickupDate: Date.parse(pickupDate), + }); + } catch (error) { + next(error); + } }, checkInItem: async (req, res, next) => { res.status(500).send({ success: false, error: "Not Implemented" }); diff --git a/src/controller/item.router.js b/src/controller/item.router.js index 174f0b1..83e4b94 100644 --- a/src/controller/item.router.js +++ b/src/controller/item.router.js @@ -1,6 +1,6 @@ const router = require("express").Router(); const controller = require("./item.controller"); -const { ItemTransactionCheck } = require("./utils/authorize"); +const { AuthenticateMiddleware, ItemTransactionCheck } = require("./utils/authorize"); /** * @route /item/ */ @@ -24,36 +24,36 @@ router.get("/:id", controller.getItemByID); /** * @route /item/:id/pick */ -router.post("/:id/pick", ItemTransactionCheck, controller.pickItem); +router.post("/:id/pick", AuthenticateMiddleware, ItemTransactionCheck, controller.pickItem); /** * @route /item/:id/put */ -router.post("/:id/put", ItemTransactionCheck, controller.putItem); +router.post("/:id/put", AuthenticateMiddleware, ItemTransactionCheck, controller.putItem); /** * @route /item/:id/reserve */ -router.post("/:id/reserve", ItemTransactionCheck, controller.reserveItem); +router.post("/:id/reserve", AuthenticateMiddleware, ItemTransactionCheck, controller.reserveItem); /** * @route /item/:id/check-in */ -router.post("/:id/check-in", ItemTransactionCheck, controller.checkInItem); +router.post("/:id/check-in", AuthenticateMiddleware, ItemTransactionCheck, controller.checkInItem); /** * @route /item/:id/check-out */ -router.post("/:id/check-out", ItemTransactionCheck, controller.checkOutItem); +router.post("/:id/check-out", AuthenticateMiddleware, ItemTransactionCheck, controller.checkOutItem); /** * @route /item/:id/report */ -router.post("/:id/report", ItemTransactionCheck, controller.reportItem); +router.post("/:id/report", AuthenticateMiddleware, ItemTransactionCheck, controller.reportItem); /** * @route /item/:id/adjust */ -router.post("/:id/adjust", ItemTransactionCheck, controller.adjustItem); +router.post("/:id/adjust", AuthenticateMiddleware, ItemTransactionCheck, controller.adjustItem); module.exports = router; diff --git a/src/models/ItemAssociation.js b/src/models/ItemAssociation.js index e84cc94..448e465 100644 --- a/src/models/ItemAssociation.js +++ b/src/models/ItemAssociation.js @@ -10,7 +10,15 @@ const schema = new mongoose.Schema( type: mongoose.Schema.Types.ObjectId, ref: "Sublevel", }, - quantity: { + totalQuantity: { + type: Number, + default: 0, + }, + reservedQuantity: { + type: Number, + default: 0, + }, + availableQuantity: { type: Number, default: 0, }, diff --git a/src/models/ItemTransaction.js b/src/models/ItemTransaction.js index e955756..708e7bd 100644 --- a/src/models/ItemTransaction.js +++ b/src/models/ItemTransaction.js @@ -33,6 +33,10 @@ const PutItemTransaction = ItemTransaction.discriminator( type: Number, required: true, }, + subLevel: { + type: mongoose.Schema.Types.ObjectId, + ref: "Sublevel" + }, }) ); @@ -43,6 +47,10 @@ const PickItemTransaction = ItemTransaction.discriminator( type: Number, required: true, }, + subLevel: { + type: mongoose.Schema.Types.ObjectId, + ref: "Sublevel", + }, }) ); From 65bdd68314a2bf856c247834aab4bb32c498cf47 Mon Sep 17 00:00:00 2001 From: Sathishkumar Krishnan Date: Fri, 7 Jan 2022 08:52:32 +0530 Subject: [PATCH 3/5] feat: added check-in, check-out & report apis --- src/config/constants.js | 3 + src/controller/item.controller.js | 102 +++++++++++++++++++++++++++--- src/models/ItemTransaction.js | 6 +- 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/src/config/constants.js b/src/config/constants.js index caa21b1..a7151a4 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -68,6 +68,8 @@ const ItemTransactionTypes = [ "RESERVE" ]; +const ReportItemForTypes = ["LOCATION", "ISSUE", "INCIDENT"]; + module.exports = { UserActions, InventoryScopes, @@ -85,4 +87,5 @@ module.exports = { AUTHENTICATION_FAILURE_ERROR_MESSAGE, AUTHORIZATION_FAILURE_ERROR_MESSAGE, ItemTransactionTypes, + ReportItemForTypes, }; diff --git a/src/controller/item.controller.js b/src/controller/item.controller.js index d5f2ee5..105ecd8 100644 --- a/src/controller/item.controller.js +++ b/src/controller/item.controller.js @@ -6,15 +6,15 @@ const { PickItemTransaction, PutItemTransaction, ReserveItemTransaction, - // CheckInItemTransaction, - // CheckOutItemTransaction, - // ReportItemTransaction, + CheckInItemTransaction, + CheckOutItemTransaction, + ReportItemTransaction, // AdjustItemTransaction, } = require("../models/ItemTransaction"); const ItemAssociation = require("../models/ItemAssociation"); const Sublevel = require("../models/Sublevel"); -const { InventoryTypes } = require("../config/constants"); +const { InventoryTypes, ReportItemForTypes } = require("../config/constants"); module.exports = { /** @@ -306,7 +306,7 @@ module.exports = { await itemAssociation.save(); await ReserveItemTransaction.create({ - type: "PUT", + type: "RESERVE", performedOn: item, performedBy: res.locals.user, reserveQuantity: reserveQuantity, @@ -318,13 +318,99 @@ module.exports = { } }, checkInItem: async (req, res, next) => { - res.status(500).send({ success: false, error: "Not Implemented" }); + try { + const { id } = req.params; + if (!id || mongoose.isValidObjectId(id)) { + res.status(400).send("Missing/Invalid id param"); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).send("item not found"); + return; + } + + const { checkInMeterReading, hasIssue, issueDescription } = req.body; + if (!(checkInMeterReading && checkInMeterReading > 0)) { + res.status(400).send("Invalid value for checkInMeterReading"); + return; + } + + await CheckInItemTransaction.create({ + type: "CHECK-IN", + performedOn: item, + performedBy: res.locals.user, + checkInMeterReading: checkInMeterReading, + hasIssue: hasIssue, + issueDescription: hasIssue ? issueDescription : "", + }); + } catch (error) { + next(error); + } }, checkOutItem: async (req, res, next) => { - res.status(500).send({ success: false, error: "Not Implemented" }); + try { + const { id } = req.params; + if (!id || mongoose.isValidObjectId(id)) { + res.status(400).send("Missing/Invalid id param"); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).send("item not found"); + return; + } + + const { checkOutMeterReading, job, usageReason } = req.body; + if (!(checkOutMeterReading && checkOutMeterReading > 0) || !(job && mongoose.isValidObjectId(job))) { + res.status(400).send("Invalid value for checkOutMeterReading/job"); + return; + } + + await CheckOutItemTransaction.create({ + type: "CHECK-OUT", + performedOn: item, + performedBy: res.locals.user, + checkOutMeterReading: checkOutMeterReading, + job: job, + usageReason: usageReason ? usageReason : "", + }); + } catch (error) { + next(error); + } }, reportItem: async (req, res, next) => { - res.status(500).send({ success: false, error: "Not Implemented" }); + try { + const { id } = req.params; + if (!id || mongoose.isValidObjectId(id)) { + res.status(400).send("Missing/Invalid id param"); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).send("item not found"); + return; + } + + const { reportingFor, details } = req.body; + if (!(reportingFor && ReportItemForTypes.includes(reportingFor))) { + res.status(400).send("Invalid value for checkOutMeterReading/job"); + return; + } + + await ReportItemTransaction.create({ + type: "PUT", + performedOn: item, + performedBy: res.locals.user, + reportingFor: reportingFor, + details: details ? details : "", + }); + } catch (error) { + next(error); + } }, adjustItem: async (req, res, next) => { res.status(500).send({ success: false, error: "Not Implemented" }); diff --git a/src/models/ItemTransaction.js b/src/models/ItemTransaction.js index 708e7bd..d670589 100644 --- a/src/models/ItemTransaction.js +++ b/src/models/ItemTransaction.js @@ -1,5 +1,5 @@ const mongoose = require("mongoose"); -const { ItemTransactionTypes } = require("../config/constants"); +const { ItemTransactionTypes, ReportItemForTypes } = require("../config/constants"); const schema = new mongoose.Schema( { @@ -111,10 +111,10 @@ const CheckOutItemTransaction = ItemTransaction.discriminator( const ReportItemTransaction = ItemTransaction.discriminator( "Report", new mongoose.Schema({ - for: { + reportingFor: { type: String, required: true, - enum: ["LOCATION", "ISSUE", "INCIDENT"] + enum: ReportItemForTypes, }, details: { type: String, From aa0933102e57e6839abedc47d1400421635686f9 Mon Sep 17 00:00:00 2001 From: Sathishkumar Krishnan Date: Fri, 7 Jan 2022 09:02:05 +0530 Subject: [PATCH 4/5] feat: added adjust api --- src/controller/item.controller.js | 53 +++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/controller/item.controller.js b/src/controller/item.controller.js index 105ecd8..8ea9ddb 100644 --- a/src/controller/item.controller.js +++ b/src/controller/item.controller.js @@ -206,7 +206,7 @@ module.exports = { next(error); } }, - pickItem: async (req, res, next) => { + putItem: async (req, res, next) => { try { const { id } = req.params; if (!id || mongoose.isValidObjectId(id)) { @@ -243,7 +243,8 @@ module.exports = { next(error); } }, - putItem: async (req, res, next) => { + + pickItem: async (req, res, next) => { try { const { id } = req.params; if (!id || mongoose.isValidObjectId(id)) { @@ -280,6 +281,7 @@ module.exports = { next(error); } }, + reserveItem: async (req, res, next) => { try { const { id } = req.params; @@ -317,6 +319,7 @@ module.exports = { next(error); } }, + checkInItem: async (req, res, next) => { try { const { id } = req.params; @@ -349,6 +352,7 @@ module.exports = { next(error); } }, + checkOutItem: async (req, res, next) => { try { const { id } = req.params; @@ -381,6 +385,7 @@ module.exports = { next(error); } }, + reportItem: async (req, res, next) => { try { const { id } = req.params; @@ -412,7 +417,49 @@ module.exports = { next(error); } }, + adjustItem: async (req, res, next) => { - res.status(500).send({ success: false, error: "Not Implemented" }); + try { + const { id } = req.params; + if (!id || mongoose.isValidObjectId(id)) { + res.status(400).send("Missing/Invalid id param"); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).send("item not found"); + return; + } + + const { recountedQuantity, damagedQuantity, subLevel } = req.body; + if (!(recountedQuantity && recountedQuantity > 0) || !(subLevel && mongoose.isValidObjectId(subLevel))) { + res.status(400).send("Invalid value for pickupQuantity/subLevel"); + return; + } + + const subLevelObj = await Sublevel.findById(subLevel); + const itemAssociation = await ItemAssociation.findOne({ item_id: item._id, sub_level_id: subLevelObj._id }); + const lastRecordedQuantity = itemAssociation.totalQuantity; + const varianceRecordedInQuantity = itemAssociation.totalQuantity - recountedQuantity; + const totalAdjustment = varianceRecordedInQuantity + damagedQuantity; + itemAssociation.totalQuantity = itemAssociation.totalQuantity - totalAdjustment; + itemAssociation.availableQuantity = itemAssociation.availableQuantity - totalAdjustment; + await itemAssociation.save(); + + await PutItemTransaction.create({ + type: "PUT", + performedOn: item, + performedBy: res.locals.user, + lastRecordedQuantity, + recountedQuantity, + varianceRecordedInQuantity, + damagedQuantity, + totalAdjustment, + newAdjustedQuantity: itemAssociation.totalQuantity, + }); + } catch (error) { + next(error); + } }, }; From 88aab63a474a0bc7a90e297e5f0d135fd34a9b60 Mon Sep 17 00:00:00 2001 From: Sathishkumar Krishnan Date: Fri, 7 Jan 2022 09:04:39 +0530 Subject: [PATCH 5/5] fix: model incorrect usage --- src/controller/item.controller.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/controller/item.controller.js b/src/controller/item.controller.js index 8ea9ddb..950239d 100644 --- a/src/controller/item.controller.js +++ b/src/controller/item.controller.js @@ -9,7 +9,7 @@ const { CheckInItemTransaction, CheckOutItemTransaction, ReportItemTransaction, - // AdjustItemTransaction, + AdjustItemTransaction, } = require("../models/ItemTransaction"); const ItemAssociation = require("../models/ItemAssociation"); @@ -232,8 +232,8 @@ module.exports = { itemAssociation.availableQuantity = itemAssociation.availableQuantity + putQuantity; await itemAssociation.save(); - await PickItemTransaction.create({ - type: "PICK", + await PutItemTransaction.create({ + type: "PUT", performedOn: item, performedBy: res.locals.user, putQuantity: putQuantity, @@ -270,8 +270,8 @@ module.exports = { itemAssociation.availableQuantity = itemAssociation.availableQuantity - pickupQuantity; await itemAssociation.save(); - await PutItemTransaction.create({ - type: "PUT", + await PickItemTransaction.create({ + type: "PICK", performedOn: item, performedBy: res.locals.user, pickupQuantity: pickupQuantity, @@ -407,7 +407,7 @@ module.exports = { } await ReportItemTransaction.create({ - type: "PUT", + type: "REPORT", performedOn: item, performedBy: res.locals.user, reportingFor: reportingFor, @@ -447,8 +447,8 @@ module.exports = { itemAssociation.availableQuantity = itemAssociation.availableQuantity - totalAdjustment; await itemAssociation.save(); - await PutItemTransaction.create({ - type: "PUT", + await AdjustItemTransaction.create({ + type: "ADJUST", performedOn: item, performedBy: res.locals.user, lastRecordedQuantity,