committed by
GitHub
parent
52d020717c
commit
4763da2d69
@@ -2,4 +2,8 @@ API_PORT=9000
|
||||
MONGODB_URI=
|
||||
JWT_SECRET=
|
||||
JWT_REFRESH_EXPIRY_TIME=
|
||||
JWT_ACCESS_EXPIRY_TIME=
|
||||
JWT_ACCESS_EXPIRY_TIME=
|
||||
AWS_S3_BUCKET=
|
||||
AWS_S3_ACCESS_KEY_ID=
|
||||
AWS_S3_SECRET_ACCESS_KEY=
|
||||
AWS_S3_REGION=
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -104,4 +104,5 @@ dist
|
||||
.tern-port
|
||||
|
||||
|
||||
uploads/*
|
||||
uploads/*
|
||||
tmp/uploads/*
|
||||
203
package-lock.json
generated
203
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.1082.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^10.0.0",
|
||||
@@ -277,6 +278,45 @@
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"node_modules/aws-sdk": {
|
||||
"version": "2.1082.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1082.0.tgz",
|
||||
"integrity": "sha512-aDrUZ63O/ocuC827ursDqFQAm3jhqsJu1DvMCCFg73y+FK9pXXNHp2mwdi3UeeHvtfxISCLCjuyO3VFd/tpVfA==",
|
||||
"dependencies": {
|
||||
"buffer": "4.9.2",
|
||||
"events": "1.1.1",
|
||||
"ieee754": "1.1.13",
|
||||
"jmespath": "0.16.0",
|
||||
"querystring": "0.2.0",
|
||||
"sax": "1.2.1",
|
||||
"url": "0.10.3",
|
||||
"uuid": "3.3.2",
|
||||
"xml2js": "0.4.19"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-sdk/node_modules/buffer": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
|
||||
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4",
|
||||
"isarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-sdk/node_modules/ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"node_modules/aws-sdk/node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -1218,6 +1258,14 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz",
|
||||
@@ -1839,6 +1887,14 @@
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jmespath": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
|
||||
"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
@@ -2684,6 +2740,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
|
||||
"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -2870,6 +2935,11 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
|
||||
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
@@ -3289,6 +3359,15 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
||||
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
|
||||
"dependencies": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse-lax": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
@@ -3301,6 +3380,11 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/url/node_modules/punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -3314,6 +3398,15 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
|
||||
@@ -3443,6 +3536,23 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/xml2js": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
||||
"dependencies": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~9.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
||||
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@@ -3658,6 +3768,44 @@
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.1082.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1082.0.tgz",
|
||||
"integrity": "sha512-aDrUZ63O/ocuC827ursDqFQAm3jhqsJu1DvMCCFg73y+FK9pXXNHp2mwdi3UeeHvtfxISCLCjuyO3VFd/tpVfA==",
|
||||
"requires": {
|
||||
"buffer": "4.9.2",
|
||||
"events": "1.1.1",
|
||||
"ieee754": "1.1.13",
|
||||
"jmespath": "0.16.0",
|
||||
"querystring": "0.2.0",
|
||||
"sax": "1.2.1",
|
||||
"url": "0.10.3",
|
||||
"uuid": "3.3.2",
|
||||
"xml2js": "0.4.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
|
||||
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4",
|
||||
"isarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
}
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -4379,6 +4527,11 @@
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||
},
|
||||
"events": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz",
|
||||
@@ -4854,6 +5007,11 @@
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"jmespath": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
|
||||
"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
@@ -5515,6 +5673,11 @@
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz",
|
||||
"integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ=="
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -5647,6 +5810,11 @@
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
|
||||
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
@@ -5981,6 +6149,22 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
||||
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
|
||||
"requires": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"url-parse-lax": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
@@ -6000,6 +6184,11 @@
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
|
||||
@@ -6096,6 +6285,20 @@
|
||||
"integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
||||
"requires": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~9.0.1"
|
||||
}
|
||||
},
|
||||
"xmlbuilder": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
||||
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/kfnawaz/plaidware-wms-core#readme",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.1082.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^10.0.0",
|
||||
|
||||
44
src/config/aws.js
Normal file
44
src/config/aws.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const AWS = require("aws-sdk");
|
||||
const fs = require("fs");
|
||||
const { AWS_S3_ACCESS_KEY_ID, AWS_S3_SECRET_ACCESS_KEY, AWS_S3_BUCKET, AWS_S3_REGION } = require("./env");
|
||||
|
||||
AWS.config.update({
|
||||
maxRetries: 3,
|
||||
accessKeyId: AWS_S3_ACCESS_KEY_ID,
|
||||
secretAccessKey: AWS_S3_SECRET_ACCESS_KEY,
|
||||
region: AWS_S3_REGION,
|
||||
});
|
||||
|
||||
const S3 = new AWS.S3();
|
||||
|
||||
module.exports = {
|
||||
S3: {
|
||||
uploadFile: async (key, filepath) => {
|
||||
const fileReadStream = fs.createReadStream(filepath);
|
||||
const params = {
|
||||
Bucket: AWS_S3_BUCKET,
|
||||
Key: key,
|
||||
Body: fileReadStream,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await S3.upload(params).promise();
|
||||
console.log("S3 Upload success", response);
|
||||
fs.rmSync(filepath);
|
||||
return response.Location;
|
||||
} catch (error) {
|
||||
console.log("S3 Upload Error", error);
|
||||
fs.rmSync(filepath);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
generatePresignedUrl: (url) => {
|
||||
const key = url.split(".com/")[1];
|
||||
return S3.getSignedUrl("getObject", {
|
||||
Bucket: AWS_S3_BUCKET,
|
||||
Key: key,
|
||||
Expires: 600,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -4,9 +4,12 @@ const envVariables = {
|
||||
API_PORT: process.env.API_PORT || "3000",
|
||||
MONGODB_URI: process.env.MONGODB_URI || "mongodb://localhost:12017",
|
||||
JWT_SECRET: process.env.JWT_SECRET || "secret123",
|
||||
JWT_REFRESH_EXPIRY_TIME:
|
||||
parseInt(process.env.JWT_REFRESH_EXPIRY_TIME) || 3600,
|
||||
JWT_REFRESH_EXPIRY_TIME: parseInt(process.env.JWT_REFRESH_EXPIRY_TIME) || 3600,
|
||||
JWT_ACCESS_EXPIRY_TIME: parseInt(process.env.JWT_ACCESS_EXPIRY_TIME) || 86400,
|
||||
AWS_S3_BUCKET: process.env.AWS_S3_BUCKET,
|
||||
AWS_S3_ACCESS_KEY_ID: process.env.AWS_S3_ACCESS_KEY_ID,
|
||||
AWS_S3_SECRET_ACCESS_KEY: process.env.AWS_S3_SECRET_ACCESS_KEY,
|
||||
AWS_S3_REGION: process.env.AWS_S3_REGION || "us-east-2",
|
||||
};
|
||||
|
||||
module.exports = envVariables;
|
||||
|
||||
@@ -10,6 +10,7 @@ const Sublevel = require("../models/Sublevel");
|
||||
const Inventory = require("../models/Inventory");
|
||||
const WidgetFamily = require("../models/WidgetFamily");
|
||||
const Item = require("../models/Item");
|
||||
const { deleteSubLevelTreeFromRoot } = require("./utils/sublevel");
|
||||
|
||||
const createWarehouse = async ({ name, address, specs, company_id }) => {
|
||||
if (!(name && address)) {
|
||||
@@ -251,20 +252,20 @@ const createWidgetFamilies = async (widgetFamilies, inventory, parent = undefine
|
||||
return widgetFamiliesData;
|
||||
};
|
||||
|
||||
const getChildModel = (parentType) => {
|
||||
switch (parentType) {
|
||||
const getLocationTypeModel = (type) => {
|
||||
switch (type) {
|
||||
case "warehouse":
|
||||
return Zone;
|
||||
return Warehouse;
|
||||
case "zone":
|
||||
return Area;
|
||||
return Zone;
|
||||
case "area":
|
||||
return Row;
|
||||
return Area;
|
||||
case "row":
|
||||
return Bay;
|
||||
return Row;
|
||||
case "bay":
|
||||
return Level;
|
||||
return Bay;
|
||||
case "level":
|
||||
return Sublevel;
|
||||
return Level;
|
||||
case "sublevel":
|
||||
return Sublevel;
|
||||
default:
|
||||
@@ -272,6 +273,26 @@ const getChildModel = (parentType) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getChildType = (parentType) => {
|
||||
switch (parentType) {
|
||||
case "warehouse":
|
||||
return "zone";
|
||||
case "zone":
|
||||
return "area";
|
||||
case "area":
|
||||
return "row";
|
||||
case "row":
|
||||
return "bay";
|
||||
case "bay":
|
||||
return "level";
|
||||
case "level":
|
||||
case "sublevel":
|
||||
return "sublevel";
|
||||
default:
|
||||
throw new Error("Invalid model type");
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createWarehouseSchema: async (req, res, next) => {
|
||||
try {
|
||||
@@ -356,13 +377,53 @@ module.exports = {
|
||||
const { id, type } = req.body;
|
||||
if (!id || !type) return res.send({ success: false, message: "Missing id or type" });
|
||||
|
||||
const query = {};
|
||||
query[`${type}_id`] = id;
|
||||
let query = {};
|
||||
|
||||
switch (type) {
|
||||
case "level":
|
||||
case "sublevel":
|
||||
query = { $or: [{ main_level_id: id, parent_sublevel_id: id }] };
|
||||
break;
|
||||
|
||||
default:
|
||||
query[`${type}_id`] = id;
|
||||
break;
|
||||
}
|
||||
|
||||
let childrenData = await getLocationTypeModel(getChildType(type)).find(query);
|
||||
|
||||
// populate locations to sublevel
|
||||
if (childrenData && ["level", "sublevel"].includes(type)) {
|
||||
const parentData = type === "level" ? await Level.findById(id) : await Sublevel.findById(id);
|
||||
childrenData =
|
||||
parentData &&
|
||||
childrenData.map((child) => ({
|
||||
...child?._doc,
|
||||
positions: parentData.sub_levels?.find((t2) => t2.sub_level_id.toString() === child._id.toString())?.positions,
|
||||
}));
|
||||
}
|
||||
|
||||
const childrenData = await getChildModel(type).find(query);
|
||||
res.send({ success: true, data: { parent: { id, type }, childrenData } });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
deleteByIdAndLocationType: async (req, res, next) => {
|
||||
const { id, type } = req.body;
|
||||
let removedIDs;
|
||||
try {
|
||||
switch (type) {
|
||||
case "sublevel":
|
||||
removedIDs = await deleteSubLevelTreeFromRoot(id);
|
||||
break;
|
||||
|
||||
default:
|
||||
removedIDs = await getLocationTypeModel(type).deleteOne({ _id: id });
|
||||
break;
|
||||
}
|
||||
res.send({ success: true, data: { removedIDs, deleted: { id, type } } });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -16,4 +16,9 @@ router.post("/create-inventory-schema", controller.createInventorySchema);
|
||||
*/
|
||||
router.post("/get-children-from-parent", controller.getChildrenFromParent);
|
||||
|
||||
/**
|
||||
* @route /dashboard/location
|
||||
*/
|
||||
router.post("/delete-location", controller.deleteByIdAndLocationType);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const Inventory = require("../models/Inventory");
|
||||
const WidgetFamily = require("../models/WidgetFamily");
|
||||
const { InventoryTypes } = require("../config/constants");
|
||||
@@ -17,16 +19,40 @@ 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 inventoryData = await Inventory.findById(id);
|
||||
if (!inventoryData) {
|
||||
res.status(404).send({ success: false, error: "Inventory not found" });
|
||||
return;
|
||||
}
|
||||
res.send({ success: true, data: inventoryData });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
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()
|
||||
.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);
|
||||
@@ -37,10 +63,10 @@ module.exports = {
|
||||
* Create a Inventory
|
||||
*/
|
||||
createInventory: async (req, res, next) => {
|
||||
const { name, type, policies } = req.body;
|
||||
let { name, policies, widgetName, icon_slug } = req.body;
|
||||
|
||||
if (!(name && type)) {
|
||||
res.status(400).send("Missing params param");
|
||||
if (!(name && widgetName && icon_slug)) {
|
||||
res.status(400).send("Missing params");
|
||||
return;
|
||||
}
|
||||
const preferredLocations = [];
|
||||
@@ -48,33 +74,32 @@ module.exports = {
|
||||
for (const preferredLocation of policies.preferredLocations) {
|
||||
preferredLocations.push({ id: preferredLocation.id, type: preferredLocation.type });
|
||||
}
|
||||
} else if (!policies) {
|
||||
policies = {};
|
||||
}
|
||||
|
||||
const verifiedPolicies = {
|
||||
orderTracking: policies.orderTracking || {},
|
||||
alerting: {
|
||||
lowestStockLevel: policies.alerting && policies.alerting.lowestStockLevel ? policies.alerting.lowestStockLevel : false,
|
||||
highestStockLevel: policies.alerting && policies.alerting.highestStockLevel ? policies.alerting.highestStockLevel : false,
|
||||
alertStockLevel: policies.alerting && policies.alerting.alertStockLevel ? policies.alerting.alertStockLevel : false,
|
||||
reOrderLevel: policies.alerting && policies.alerting.reOrderLevel ? policies.alerting.reOrderLevel : false,
|
||||
},
|
||||
replenishment: policies.replenishment || {},
|
||||
preferredLocations: preferredLocations,
|
||||
orderTracking: policies.orderTracking || false,
|
||||
alerting: policies.alerting || false,
|
||||
replenishment: policies.replenishment || false,
|
||||
preferredLocations: preferredLocations || false,
|
||||
inventory_process: policies.inventory_process,
|
||||
};
|
||||
|
||||
try {
|
||||
const inventoryData = new Inventory({
|
||||
name,
|
||||
type,
|
||||
widgetName,
|
||||
policies: verifiedPolicies,
|
||||
icon_slug,
|
||||
});
|
||||
|
||||
await inventoryData.save();
|
||||
if (!inventoryData) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
res.send({ success: true, data: inventoryData });
|
||||
await inventoryData.save();
|
||||
res.send({ success: true, data: { inventoryData } });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -84,65 +109,49 @@ module.exports = {
|
||||
* Update a Inventory detail
|
||||
*/
|
||||
updateInventoryByID: async (req, res, next) => {
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id) {
|
||||
res.status(400).send("Missing id param");
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, type, policies } = req.body;
|
||||
|
||||
if (!(name || type)) {
|
||||
res.status(400).send("Missing data in body");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const inventoryData = await Inventory.findById(id);
|
||||
if (!inventoryData) {
|
||||
res.status(404);
|
||||
const { id } = req.params;
|
||||
|
||||
if (!(id && mongoose.isValidObjectId(id))) {
|
||||
res.status(400).send("Missing/Improper id param");
|
||||
return;
|
||||
}
|
||||
|
||||
if (name) inventoryData.name = name;
|
||||
if (type) inventoryData.type = type;
|
||||
const inventory = await Inventory.findById(id);
|
||||
|
||||
if (policies) {
|
||||
const preferredLocations = [];
|
||||
if (policies.preferredLocations && Array.isArray(policies.preferredLocations)) {
|
||||
for (const preferredLocation of policies.preferredLocations) {
|
||||
preferredLocations.push({ id: preferredLocation.id, type: preferredLocation.type });
|
||||
}
|
||||
}
|
||||
|
||||
inventoryData.policies = {
|
||||
orderTracking: policies.orderTracking || inventoryData.policies.orderTracking,
|
||||
alerting: {
|
||||
lowestStockLevel:
|
||||
policies.alerting && policies.alerting.lowestStockLevel !== undefined
|
||||
? policies.alerting.lowestStockLevel
|
||||
: inventoryData.policies.alerting.lowestStockLevel,
|
||||
highestStockLevel:
|
||||
policies.alerting && policies.alerting.highestStockLevel !== undefined
|
||||
? policies.alerting.highestStockLevel
|
||||
: inventoryData.policies.alerting.highestStockLevel,
|
||||
alertStockLevel:
|
||||
policies.alerting && policies.alerting.alertStockLevel !== undefined
|
||||
? policies.alerting.alertStockLevel
|
||||
: inventoryData.policies.alerting.alertStockLevel,
|
||||
reOrderLevel:
|
||||
policies.alerting && policies.alerting.reOrderLevel !== undefined
|
||||
? policies.alerting.reOrderLevel
|
||||
: inventoryData.policies.alerting.reOrderLevel,
|
||||
},
|
||||
replenishment: policies.replenishment || inventoryData.policies.replenishment,
|
||||
preferredLocations: preferredLocations,
|
||||
};
|
||||
if (!inventory) {
|
||||
res.status(400).send("Inventory not found");
|
||||
return;
|
||||
}
|
||||
let { name, policies, widgetName, icon_slug } = req.body;
|
||||
if (name) {
|
||||
inventory.name = name;
|
||||
}
|
||||
|
||||
await inventoryData.save();
|
||||
res.send({ success: true, data: inventoryData });
|
||||
if (widgetName) {
|
||||
inventory.widgetName = widgetName;
|
||||
}
|
||||
|
||||
if (icon_slug) {
|
||||
inventory.icon_slug = icon_slug;
|
||||
}
|
||||
if (!policies) {
|
||||
policies = {};
|
||||
}
|
||||
|
||||
// const widgetFamilyData = createWidgetFamiliesData(inventory, widgetFamily);
|
||||
|
||||
const verifiedPolicies = {
|
||||
orderTracking: policies.orderTracking !== undefined ? policies.orderTracking : inventory.policies.orderTracking,
|
||||
alerting: policies.alerting !== undefined ? policies.alerting : inventory.policies.alerting,
|
||||
replenishment: policies.replenishment !== undefined ? policies.replenishment : inventory.policies.replenishment,
|
||||
preferredLocations: policies.replenishment !== undefined ? policies.replenishment : inventory.policies.preferredLocations,
|
||||
inventory_process: policies.inventory_process || inventory.policies.inventory_process,
|
||||
};
|
||||
|
||||
inventory.policies = verifiedPolicies;
|
||||
await inventory.save();
|
||||
res.send({ success: true, data: { inventory } });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const router = require("express").Router();
|
||||
const controller = require("./inventory.controller");
|
||||
|
||||
/**
|
||||
* @route /inventory/
|
||||
*/
|
||||
@@ -16,6 +15,11 @@ router.patch("/:id", controller.updateInventoryByID);
|
||||
*/
|
||||
router.get("/types", controller.getInventoryTypes);
|
||||
|
||||
/**
|
||||
* @route /inventory/all
|
||||
*/
|
||||
router.get("/all", controller.getInventories);
|
||||
|
||||
/**
|
||||
* @route /inventory/filter-by-type
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
@@ -11,11 +10,11 @@ const {
|
||||
ReportItemTransaction,
|
||||
AdjustItemTransaction,
|
||||
} = require("../models/ItemTransaction");
|
||||
|
||||
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);
|
||||
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);
|
||||
@@ -63,9 +68,16 @@ module.exports = {
|
||||
countPerPalletPackage: req.body.countPerPalletPackage,
|
||||
customAttributes: req.body.customAttributes,
|
||||
widgetFamily: widgetFamily,
|
||||
policiesMetadata: {
|
||||
underStockLevelCount: req.body.policiesMetadata.underStockLevelCount,
|
||||
overStockLevelCount: req.body.policiesMetadata.overStockLevelCount,
|
||||
alertStockLevelCount: req.body.policiesMetadata.alertStockLevelCount,
|
||||
reorderStockLevelCount: req.body.policiesMetadata.reorderStockLevelCount,
|
||||
},
|
||||
};
|
||||
|
||||
for (const key of Object.keys(item)) {
|
||||
if (["customAttributes"].includes(key)) continue;
|
||||
if (item[key] === undefined) {
|
||||
res.status(400).send({ success: false, error: `Missing required param: "${key}"` });
|
||||
return;
|
||||
@@ -80,6 +92,24 @@ module.exports = {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const images = req.files;
|
||||
|
||||
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 });
|
||||
}
|
||||
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);
|
||||
@@ -130,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);
|
||||
@@ -142,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);
|
||||
}
|
||||
@@ -229,7 +247,7 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { putQuantity, subLevel } = req.body;
|
||||
const { putQuantity, subLevel, usageReason, job } = req.body;
|
||||
if (!(putQuantity && putQuantity > 0) || !(subLevel && mongoose.isValidObjectId(subLevel))) {
|
||||
res.status(400).send("Invalid value for putQuantity/subLevel");
|
||||
return;
|
||||
@@ -250,6 +268,8 @@ module.exports = {
|
||||
performedBy: res.locals.user,
|
||||
putQuantity: putQuantity,
|
||||
subLevel: subLevelObj,
|
||||
usageReason: usageReason ? usageReason : "",
|
||||
job: job,
|
||||
});
|
||||
res.send({ success: true, data: { itemAssociation, itemTransaction } });
|
||||
res.send({ success: true, data: { itemAssociation, itemTransaction } });
|
||||
@@ -272,7 +292,7 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { pickupQuantity, subLevel } = req.body;
|
||||
const { pickupQuantity, subLevel, usageReason, job } = req.body;
|
||||
if (!(pickupQuantity && pickupQuantity > 0) || !(subLevel && mongoose.isValidObjectId(subLevel))) {
|
||||
res.status(400).send("Invalid value for pickupQuantity/subLevel");
|
||||
return;
|
||||
@@ -293,6 +313,8 @@ module.exports = {
|
||||
performedBy: res.locals.user,
|
||||
pickupQuantity: pickupQuantity,
|
||||
subLevel: subLevelObj,
|
||||
usageReason: usageReason ? usageReason : "",
|
||||
job: job,
|
||||
});
|
||||
res.send({ success: true, data: { itemAssociation, itemTransaction } });
|
||||
} catch (error) {
|
||||
@@ -314,15 +336,15 @@ module.exports = {
|
||||
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");
|
||||
const { reserveQuantity, job, pickupDate, usageReason } = req.body;
|
||||
if (!(reserveQuantity && reserveQuantity > 0) || !(job && mongoose.isValidObjectId(job))) {
|
||||
res.status(400).send("Invalid value for reserveQuantity/job");
|
||||
return;
|
||||
}
|
||||
|
||||
const itemAssociation = await ItemAssociation.findOne({ item_id: item._id, availableQuantity: { $gte: reserveQuantity } });
|
||||
if (!itemAssociation) {
|
||||
res.status(500).send("Quantity unavailable");
|
||||
res.status(500).send({ success: false, error: "Quantity unavailable" });
|
||||
return;
|
||||
}
|
||||
itemAssociation.reservedQuantity = itemAssociation.reservedQuantity + reserveQuantity;
|
||||
@@ -335,7 +357,8 @@ module.exports = {
|
||||
performedBy: res.locals.user,
|
||||
reserveQuantity: reserveQuantity,
|
||||
job: job,
|
||||
pickupDate: Date.parse(pickupDate),
|
||||
pickupDate: pickupDate ? Date.parse(pickupDate) : undefined,
|
||||
usageReason: usageReason ? usageReason : "",
|
||||
});
|
||||
res.send({ success: true, data: { itemAssociation, itemTransaction } });
|
||||
} catch (error) {
|
||||
@@ -357,19 +380,17 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { checkInMeterReading, hasIssue, issueDescription } = req.body;
|
||||
if (!(checkInMeterReading && checkInMeterReading > 0)) {
|
||||
res.status(400).send("Invalid value for checkInMeterReading");
|
||||
return;
|
||||
}
|
||||
const { checkInMeterReading, hasIssue, issueDescription, usageReason, job } = req.body;
|
||||
|
||||
const itemTransaction = await CheckInItemTransaction.create({
|
||||
type: "CHECK-IN",
|
||||
performedOn: item,
|
||||
performedBy: res.locals.user,
|
||||
checkInMeterReading: checkInMeterReading,
|
||||
hasIssue: hasIssue,
|
||||
hasIssue: hasIssue || false,
|
||||
issueDescription: hasIssue ? issueDescription : "",
|
||||
usageReason: usageReason ? usageReason : "",
|
||||
job: job,
|
||||
});
|
||||
res.send({ success: true, data: { itemTransaction } });
|
||||
} catch (error) {
|
||||
@@ -391,19 +412,15 @@ module.exports = {
|
||||
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;
|
||||
}
|
||||
const { checkOutMeterReading, usageReason, job } = req.body;
|
||||
|
||||
const itemTransaction = await CheckOutItemTransaction.create({
|
||||
type: "CHECK-OUT",
|
||||
performedOn: item,
|
||||
performedBy: res.locals.user,
|
||||
checkOutMeterReading: checkOutMeterReading,
|
||||
job: job,
|
||||
usageReason: usageReason ? usageReason : "",
|
||||
job: job,
|
||||
});
|
||||
res.send({ success: true, data: { itemTransaction } });
|
||||
} catch (error) {
|
||||
@@ -425,7 +442,7 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { reportingFor, details } = req.body;
|
||||
const { reportingFor, details, usageReason, job } = req.body;
|
||||
if (!(reportingFor && ReportItemForTypes.includes(reportingFor))) {
|
||||
res.status(400).send("Invalid value for checkOutMeterReading/job");
|
||||
return;
|
||||
@@ -437,6 +454,8 @@ module.exports = {
|
||||
performedBy: res.locals.user,
|
||||
reportingFor: reportingFor,
|
||||
details: details ? details : "",
|
||||
usageReason: usageReason ? usageReason : "",
|
||||
job: job,
|
||||
});
|
||||
res.send({ success: true, data: { itemTransaction } });
|
||||
} catch (error) {
|
||||
@@ -458,7 +477,7 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { recountedQuantity, damagedQuantity, subLevel } = req.body;
|
||||
const { recountedQuantity, damagedQuantity, subLevel, usageReason, job } = req.body;
|
||||
if (!(recountedQuantity && recountedQuantity > 0) || !(subLevel && mongoose.isValidObjectId(subLevel))) {
|
||||
res.status(400).send("Invalid value for pickupQuantity/subLevel");
|
||||
return;
|
||||
@@ -483,10 +502,64 @@ module.exports = {
|
||||
damagedQuantity,
|
||||
totalAdjustment,
|
||||
newAdjustedQuantity: itemAssociation.totalQuantity,
|
||||
usageReason: usageReason ? usageReason : "",
|
||||
job: job,
|
||||
});
|
||||
res.send({ success: true, data: { itemAssociation, itemTransaction } });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
addImageToItem: async (req, res, next) => {
|
||||
const { id } = req.params;
|
||||
if (!mongoose.isValidObjectId(id)) {
|
||||
res.status(400).send({ success: false, error: "invalid id" });
|
||||
}
|
||||
|
||||
const item = await Item.findById(id);
|
||||
if (!item) {
|
||||
res.status(404).send({ success: false, error: "item not found" });
|
||||
}
|
||||
|
||||
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.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 });
|
||||
},
|
||||
|
||||
removeImageFromItem: async (req, res, next) => {
|
||||
const { id, image_id } = req.params;
|
||||
if (!mongoose.isValidObjectId(id)) {
|
||||
res.status(400).send({ success: false, error: "invalid id" });
|
||||
}
|
||||
if (!mongoose.isValidObjectId(image_id)) {
|
||||
res.status(400).send({ success: false, error: "invalid image_id" });
|
||||
}
|
||||
|
||||
const item = await Item.findById(id);
|
||||
if (!item) {
|
||||
res.status(404).send({ success: false, error: "item not found" });
|
||||
}
|
||||
|
||||
item.images = item.images.filter((itemImage) => {
|
||||
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 });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,21 +1,39 @@
|
||||
const router = require("express").Router();
|
||||
const controller = require("./item.controller");
|
||||
const { AuthenticateMiddleware, ItemTransactionCheck } = require("./utils/authorize");
|
||||
/**
|
||||
* @route /item/
|
||||
*/
|
||||
router.post("/", controller.createItem);
|
||||
const multer = require("multer");
|
||||
const upload = multer({ dest: "tmp/uploads/" });
|
||||
|
||||
/**
|
||||
* @route /item/
|
||||
*/
|
||||
router.patch("/:id", controller.updateItemByID);
|
||||
router.post("/", upload.any("images"), controller.createItem);
|
||||
|
||||
/**
|
||||
* @route /item/:id/image
|
||||
*/
|
||||
router.post("/:id/image", upload.single("image"), controller.addImageToItem);
|
||||
|
||||
/**
|
||||
* @route /item/:id/image/:image_id
|
||||
*/
|
||||
router.delete("/:id/image/:image_id", controller.removeImageFromItem);
|
||||
|
||||
/**
|
||||
* @route /item/
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
const Sublevel = require("../models/Sublevel");
|
||||
const mongoose = require("mongoose");
|
||||
const { filterSublevels } = require("./utils/aggregation");
|
||||
const { addSublevelToParent, deleteSubLevelTreeFromRoot, validPositions } = require("./utils/sublevel");
|
||||
const { SubLevelTypes } = require("../config/constants");
|
||||
|
||||
const getLabelTerm = (name) => {
|
||||
return name.toString().slice(name.toString().length - 3);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Gets the sublevel data by `id`
|
||||
@@ -31,6 +36,7 @@ module.exports = {
|
||||
* Create a sublevel
|
||||
*/
|
||||
createSubLevel: async (req, res, next) => {
|
||||
console.log(req.body);
|
||||
const { name, type, specs, parent_id, parentIsLevel, positions } = req.body;
|
||||
|
||||
if (!(name && type && parent_id && positions)) {
|
||||
@@ -70,7 +76,7 @@ module.exports = {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
req.send(sublevelData);
|
||||
res.send({ ...sublevelData?._doc, positions });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -182,4 +188,44 @@ module.exports = {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
filterSublevels: async (req, res, next) => {
|
||||
const { warehouse, zone, area, row } = req.body;
|
||||
const results = await filterSublevels({ warehouse, zone, area, row });
|
||||
const resultsGroupedByBay = {};
|
||||
for (const resultItem of results) {
|
||||
if (resultsGroupedByBay[resultItem.bay._id]) {
|
||||
resultsGroupedByBay[resultItem.bay._id].location_data.push({
|
||||
level: { id: resultItem.level._id, name: resultItem.level.name },
|
||||
sublevel: { id: resultItem.sublevel1._id, name: resultItem.sublevel1.name },
|
||||
label: `Z${getLabelTerm(resultItem.zone._id)}-A${getLabelTerm(resultItem.area._id)}-R${getLabelTerm(resultItem.row._id)}-B${getLabelTerm(
|
||||
resultItem.bay._id
|
||||
)}-L${getLabelTerm(resultItem.level._id)}-${resultItem.sublevel1.name}`,
|
||||
});
|
||||
} else {
|
||||
resultsGroupedByBay[resultItem.bay._id] = {
|
||||
warehouse: { id: resultItem._id, name: resultItem.name },
|
||||
zone: { id: resultItem.zone._id, name: resultItem.area.name },
|
||||
area: { id: resultItem.area._id, name: resultItem.area.name },
|
||||
row: { id: resultItem.row._id, name: resultItem.row.name },
|
||||
bay: { id: resultItem.bay._id, name: resultItem.bay.name },
|
||||
totem_label: [
|
||||
`Z${getLabelTerm(resultItem.zone._id)}-A${getLabelTerm(resultItem.area._id)}-R${getLabelTerm(resultItem.row._id)}-B${getLabelTerm(
|
||||
resultItem.bay._id
|
||||
)}}`,
|
||||
],
|
||||
location_data: [
|
||||
{
|
||||
level: { id: resultItem.level._id, name: resultItem.level.name },
|
||||
sublevel: { id: resultItem.sublevel1._id, name: resultItem.sublevel1.name },
|
||||
label: `Z${getLabelTerm(resultItem.zone._id)}-A${getLabelTerm(resultItem.area._id)}-R${getLabelTerm(
|
||||
resultItem.row._id
|
||||
)}-B${getLabelTerm(resultItem.bay._id)}-L${getLabelTerm(resultItem.level._id)}-${resultItem.sublevel1.name}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
res.send({ success: true, data: Object.values(resultsGroupedByBay) });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
const router = require("express").Router();
|
||||
const controller = require("./sublevel.controller");
|
||||
|
||||
/**
|
||||
* @route /sublevel/filter
|
||||
*/
|
||||
router.post("/filter", controller.filterSublevels);
|
||||
|
||||
/**
|
||||
* @route /sublevel/
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const bcrypt = require("bcrypt");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const User = require("./../models/User");
|
||||
const { JWT_SECRET, JWT_REFRESH_EXPIRY_TIME, JWT_ACCESS_EXPIRY_TIME } = require("./../config/env");
|
||||
const UserRole = require("../models/UserRole");
|
||||
const UserPermission = require("../models/UserPermission");
|
||||
const { AllUIModules } = require("../config/constants");
|
||||
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, {
|
||||
@@ -89,48 +89,86 @@ module.exports = {
|
||||
|
||||
addUserAccessControl: async (req, res, next) => {
|
||||
const { user } = req.params;
|
||||
const { roles, permissions } = req.body;
|
||||
const {
|
||||
roles,
|
||||
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
|
||||
} = req.body;
|
||||
if (!mongoose.isValidObjectId(user)) {
|
||||
throw new Error(`invalid format for user id field`);
|
||||
}
|
||||
const userObject = await User.findById(user);
|
||||
if (!userObject) {
|
||||
res.status(404).send({ success: false, error: "User not found" });
|
||||
}
|
||||
|
||||
let verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
let verifiedPermissionIds = await getValidIds(permissions, UserPermission);
|
||||
verifiedRoleIds = verifiedRoleIds || [];
|
||||
verifiedPermissionIds = verifiedPermissionIds || [];
|
||||
if (roles) {
|
||||
let verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
verifiedRoleIds = verifiedRoleIds || [];
|
||||
userObject.roles = Array.from(new Set([...userObject.roles, ...verifiedRoleIds]));
|
||||
}
|
||||
|
||||
const response = await User.findByIdAndUpdate(
|
||||
user,
|
||||
{
|
||||
$push: {
|
||||
roles: { $each: verifiedRoleIds },
|
||||
permissions: { $each: verifiedPermissionIds },
|
||||
},
|
||||
},
|
||||
{ returnDocument: "after" }
|
||||
);
|
||||
res.send({ success: true, data: response });
|
||||
if (inventoryScopes) {
|
||||
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
|
||||
userObject.permissions.inventoryScopes = Array.from(new Set([...userObject.permissions.inventoryScopes, ...verifiedInventoryScopes]));
|
||||
}
|
||||
if (warehouseScopes) {
|
||||
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
|
||||
userObject.permissions.warehouseScopes = Array.from(new Set([...userObject.permissions.warehouseScopes, ...verifiedWarehouseScopes]));
|
||||
}
|
||||
if (actions) {
|
||||
userObject.permissions.actions = Array.from(new Set([...userObject.permissions.actions, ...actions.filter((_) => UserActions.includes(_))]));
|
||||
}
|
||||
if (allowedUIModules) {
|
||||
userObject.permissions.allowedUIModules = Array.from(
|
||||
new Set([...userObject.permissions.allowedUIModules, ...allowedUIModules.filter((_) => AllUIModules.includes(_))])
|
||||
);
|
||||
}
|
||||
await userObject.save();
|
||||
res.send({ success: true, data: userObject });
|
||||
},
|
||||
|
||||
removeUserAccessControl: async (req, res, next) => {
|
||||
const { user } = req.params;
|
||||
const { roles, permissions } = req.body;
|
||||
const {
|
||||
roles,
|
||||
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
|
||||
} = req.body;
|
||||
if (!mongoose.isValidObjectId(user)) {
|
||||
throw new Error(`invalid format for user id field`);
|
||||
}
|
||||
const verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
const verifiedPermissionIds = await getValidIds(permissions, UserPermission);
|
||||
const response = await User.findByIdAndUpdate(
|
||||
user,
|
||||
{
|
||||
$pull: {
|
||||
roles: { $in: verifiedRoleIds },
|
||||
permissions: { $in: verifiedPermissionIds },
|
||||
},
|
||||
},
|
||||
{ returnDocument: "after" }
|
||||
);
|
||||
res.send({ success: true, data: response });
|
||||
const userObject = await User.findById(user);
|
||||
if (!userObject) {
|
||||
res.status(404).send({ success: false, error: "User not found" });
|
||||
}
|
||||
|
||||
if (roles) {
|
||||
let verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
verifiedRoleIds = verifiedRoleIds || [];
|
||||
userObject.roles = userObject.roles.filter((_) => !verifiedRoleIds.includes(_));
|
||||
}
|
||||
|
||||
if (inventoryScopes) {
|
||||
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
|
||||
userObject.permissions.inventoryScopes = userObject.permissions.inventoryScopes.filter((_) => !verifiedInventoryScopes.includes(_.id));
|
||||
}
|
||||
if (warehouseScopes) {
|
||||
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
|
||||
userObject.permissions.warehouseScopes = userObject.permissions.warehouseScopes.filter((warehouseScope) => {
|
||||
for (const verifiedWarehouseScope of verifiedWarehouseScopes) {
|
||||
if (verifiedWarehouseScope.id == warehouseScope.id && verifiedWarehouseScope.type == warehouseScope.type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (actions) {
|
||||
userObject.permissions.actions = userObject.permissions.actions.filter((_) => !actions.includes(_));
|
||||
}
|
||||
if (allowedUIModules) {
|
||||
userObject.permissions.allowedUIModules = userObject.permissions.allowedUIModules.filter((_) => !allowedUIModules.includes(_));
|
||||
}
|
||||
await userObject.save();
|
||||
res.send({ success: true, data: userObject });
|
||||
},
|
||||
|
||||
getUIAccessControl: async (req, res, next) => {
|
||||
@@ -167,9 +205,11 @@ module.exports = {
|
||||
const result = await User.find()
|
||||
.skip(page * perPage)
|
||||
.limit(perPage)
|
||||
.populate({ path: "roles", populate: "permissions" })
|
||||
.populate("permissions")
|
||||
.populate("roles")
|
||||
.populate("createdBy");
|
||||
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);
|
||||
@@ -183,17 +223,20 @@ 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, fullName, password } = req.body;
|
||||
let { email, fullName, password, roles, permissions } = req.body;
|
||||
permissions ||= {};
|
||||
const { inventoryScopes, warehouseScopes, actions, allowedUIModules } = permissions;
|
||||
|
||||
try {
|
||||
const salt = await bcrypt.genSalt();
|
||||
const newUser = {
|
||||
@@ -203,10 +246,36 @@ module.exports = {
|
||||
createdBy: res.locals.user,
|
||||
};
|
||||
|
||||
if (roles) {
|
||||
let verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
verifiedRoleIds = verifiedRoleIds || [];
|
||||
newUser.roles = verifiedRoleIds;
|
||||
}
|
||||
|
||||
newUser.permissions = {};
|
||||
if (inventoryScopes) {
|
||||
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
|
||||
newUser.permissions.inventoryScopes = verifiedInventoryScopes;
|
||||
}
|
||||
if (warehouseScopes) {
|
||||
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
|
||||
newUser.permissions.warehouseScopes = verifiedWarehouseScopes;
|
||||
}
|
||||
if (actions) {
|
||||
newUser.permissions.actions = actions.filter((_) => UserActions.includes(_));
|
||||
}
|
||||
if (allowedUIModules) {
|
||||
newUser.permissions.allowedUIModules = allowedUIModules.filter((_) => AllUIModules.includes(_));
|
||||
}
|
||||
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);
|
||||
@@ -219,7 +288,9 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { email, fullName, password } = req.body;
|
||||
let { email, fullName, password, roles, permissions } = req.body;
|
||||
permissions ||= {};
|
||||
const { inventoryScopes, warehouseScopes, actions, allowedUIModules } = permissions;
|
||||
try {
|
||||
const user = await User.findById(id);
|
||||
if (!user) {
|
||||
@@ -231,10 +302,39 @@ module.exports = {
|
||||
if (email) user.email = email;
|
||||
if (fullName) user.fullName = fullName;
|
||||
if (password) user.password = await bcrypt.hash(password, salt);
|
||||
if (roles) {
|
||||
let verifiedRoleIds = await getValidIds(roles, UserRole);
|
||||
verifiedRoleIds = verifiedRoleIds || [];
|
||||
user.roles = verifiedRoleIds;
|
||||
}
|
||||
if (inventoryScopes) {
|
||||
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
|
||||
user.permissions.inventoryScopes = verifiedInventoryScopes;
|
||||
user.markModified("permissions.inventoryScopes");
|
||||
}
|
||||
if (warehouseScopes) {
|
||||
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
|
||||
user.permissions.warehouseScopes = verifiedWarehouseScopes;
|
||||
user.markModified("permissions.warehouseScopes");
|
||||
}
|
||||
if (actions) {
|
||||
user.permissions.actions = actions.filter((_) => UserActions.includes(_));
|
||||
user.markModified("permissions.actions");
|
||||
}
|
||||
if (allowedUIModules) {
|
||||
user.permissions.allowedUIModules = allowedUIModules.filter((_) => AllUIModules.includes(_));
|
||||
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,15 +1,7 @@
|
||||
const mongoose = require("mongoose");
|
||||
const UserRole = require("../models/UserRole");
|
||||
const UserPermission = require("../models/UserPermission");
|
||||
|
||||
const getValidPermissions = async (permissions) => {
|
||||
const verifiedPermissions = permissions.filter((permission) => mongoose.isValidObjectId(permission));
|
||||
if (verifiedPermissions.length === 0) return [];
|
||||
const permissionObjects = await UserPermission.find({
|
||||
_id: { $in: verifiedPermissions },
|
||||
}).select({ _id: 1 });
|
||||
return permissionObjects.map((_) => _._id);
|
||||
};
|
||||
const { getScopes } = require("./utils/access-control");
|
||||
const { InventoryScopes, WarehouseScopes, UserActions, AllUIModules } = require("./../config/constants");
|
||||
|
||||
module.exports = {
|
||||
getAllRoles: async (req, res, next) => {
|
||||
@@ -43,11 +35,22 @@ module.exports = {
|
||||
},
|
||||
createRole: async (req, res, next) => {
|
||||
try {
|
||||
const { name, permissions } = req.body;
|
||||
const verifiedPermissions = await getValidPermissions(permissions);
|
||||
const {
|
||||
name,
|
||||
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
|
||||
status,
|
||||
} = req.body;
|
||||
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
|
||||
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
|
||||
const newUserRole = await UserRole.create({
|
||||
name,
|
||||
permissions: verifiedPermissions,
|
||||
status,
|
||||
permissions: {
|
||||
inventoryScopes: verifiedInventoryScopes,
|
||||
warehouseScopes: verifiedWarehouseScopes,
|
||||
actions: actions == undefined ? [] : actions.filter((_) => UserActions.includes(_)),
|
||||
allowedUIModules: allowedUIModules == undefined ? [] : allowedUIModules.filter((_) => AllUIModules.includes(_)),
|
||||
},
|
||||
});
|
||||
res.send({ success: true, data: newUserRole });
|
||||
} catch (e) {
|
||||
@@ -55,8 +58,48 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
updateRole: async (req, res, next) => {
|
||||
// Need more clarity
|
||||
res.send({ success: false, error: "not implemented" });
|
||||
try {
|
||||
const { id } = req.params;
|
||||
if (!(id && mongoose.isValidObjectId(id))) {
|
||||
res.status(400).send({ success: false, error: "invalid Id params" });
|
||||
}
|
||||
const role = await UserRole.findById(id);
|
||||
if (!role) {
|
||||
res.status(404).send({ success: false, error: "Role not found" });
|
||||
}
|
||||
|
||||
const {
|
||||
name,
|
||||
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
|
||||
} = req.body;
|
||||
|
||||
if (name) {
|
||||
role.name = name;
|
||||
role.markModified("name");
|
||||
}
|
||||
if (inventoryScopes) {
|
||||
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
|
||||
role.permissions.inventoryScopes = verifiedInventoryScopes;
|
||||
role.markModified("permissions.inventoryScopes");
|
||||
}
|
||||
if (warehouseScopes) {
|
||||
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
|
||||
role.permissions.warehouseScopes = verifiedWarehouseScopes;
|
||||
role.markModified("permissions.warehouseScopes");
|
||||
}
|
||||
if (actions) {
|
||||
role.permissions.actions = actions.filter((_) => UserActions.includes(_));
|
||||
role.markModified("permissions.actions");
|
||||
}
|
||||
if (allowedUIModules) {
|
||||
role.permissions.allowedUIModules = allowedUIModules.filter((_) => AllUIModules.includes(_));
|
||||
role.markModified("permissions.allowedUIModules");
|
||||
}
|
||||
await role.save();
|
||||
res.send({ success: true, data: role });
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
},
|
||||
deleteRole: async (req, res, next) => {
|
||||
try {
|
||||
|
||||
27
src/controller/utils/access-control.js
Normal file
27
src/controller/utils/access-control.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
module.exports = {
|
||||
getScopes: async (scopes, searchSet) => {
|
||||
const verifiedScopes = [];
|
||||
if (scopes !== undefined && Array.isArray(scopes)) {
|
||||
for (const scope of scopes) {
|
||||
if (mongoose.isValidObjectId(scope.id)) {
|
||||
if (scope.type !== undefined && searchSet.includes(scope.type)) {
|
||||
const model = require(`../../models/${scope.type}`);
|
||||
const inventoryObject = await model.findById(scope.id);
|
||||
if (inventoryObject == undefined) {
|
||||
continue;
|
||||
}
|
||||
verifiedScopes.push({
|
||||
id: inventoryObject._id,
|
||||
type: scope.type,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error(`invalid data format for object-id - ${scope.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return verifiedScopes;
|
||||
},
|
||||
};
|
||||
241
src/controller/utils/aggregation.js
Normal file
241
src/controller/utils/aggregation.js
Normal file
@@ -0,0 +1,241 @@
|
||||
const mongoose = require("mongoose");
|
||||
const Item = require("../../models/Item");
|
||||
const Warehouse = require("./../../models/Warehouse");
|
||||
|
||||
const lookupPipeline = (from, localField, foreignField, as) => {
|
||||
return {
|
||||
$lookup: {
|
||||
from,
|
||||
localField,
|
||||
foreignField,
|
||||
as,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const unwindPipeline = (path, preserveNullAndEmptyArrays = true) => {
|
||||
return {
|
||||
$unwind: {
|
||||
path: "$" + path,
|
||||
preserveNullAndEmptyArrays,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const sublevelFilterAggregationPipeline = (warehouse, zone, area, row) => {
|
||||
const sublevelFilterAggregationPipeline = [];
|
||||
if (warehouse && mongoose.isValidObjectId(warehouse)) {
|
||||
sublevelFilterAggregationPipeline.push({
|
||||
$match: {
|
||||
_id: mongoose.Types.ObjectId(warehouse),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
sublevelFilterAggregationPipeline.push(lookupPipeline("zones", "_id", "warehouse_id", "zone"));
|
||||
sublevelFilterAggregationPipeline.push(unwindPipeline("zone"));
|
||||
if (warehouse && zone && mongoose.isValidObjectId(zone)) {
|
||||
sublevelFilterAggregationPipeline.push({
|
||||
$match: {
|
||||
"zone._id": mongoose.Types.ObjectId(zone),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
sublevelFilterAggregationPipeline.push(lookupPipeline("areas", "zone._id", "zone_id", "area"));
|
||||
sublevelFilterAggregationPipeline.push(unwindPipeline("area"));
|
||||
if (warehouse && zone && area && mongoose.isValidObjectId(area)) {
|
||||
sublevelFilterAggregationPipeline.push({
|
||||
$match: {
|
||||
"area._id": mongoose.Types.ObjectId(area),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
sublevelFilterAggregationPipeline.push(lookupPipeline("rows", "area._id", "area_id", "row"));
|
||||
sublevelFilterAggregationPipeline.push(unwindPipeline("row"));
|
||||
if (warehouse && zone && area && row && mongoose.isValidObjectId(row)) {
|
||||
sublevelFilterAggregationPipeline.push({
|
||||
$match: {
|
||||
"row._id": mongoose.Types.ObjectId(row),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
sublevelFilterAggregationPipeline.push(lookupPipeline("bays", "row._id", "row_id", "bay"));
|
||||
sublevelFilterAggregationPipeline.push(unwindPipeline("bay"));
|
||||
sublevelFilterAggregationPipeline.push(lookupPipeline("levels", "bay._id", "bay_id", "level"));
|
||||
sublevelFilterAggregationPipeline.push(unwindPipeline("level"));
|
||||
sublevelFilterAggregationPipeline.push(lookupPipeline("sublevels", "level._id", "main_level_id", "sublevel1"));
|
||||
sublevelFilterAggregationPipeline.push(unwindPipeline("sublevel1"));
|
||||
// sublevelFilterAggregationPipeline.push(lookupPipeline("sublevels", "sublevel1._id", "parent_id", "sublevel2"));
|
||||
// sublevelFilterAggregationPipeline.push(unwindPipeline("sublevel2", false));
|
||||
// sublevelFilterAggregationPipeline.push(lookupPipeline("sublevels", "sublevel2._id", "parent_id", "sublevel3"));
|
||||
// sublevelFilterAggregationPipeline.push(unwindPipeline("sublevel3", false));
|
||||
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,15 +8,16 @@ 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");
|
||||
}
|
||||
};
|
||||
|
||||
const authorize = async (user, requiredRoles = [], requiredPermissions = []) => {
|
||||
const userRoles = user.roles.map((_) => _._id);
|
||||
const userPermissions = [...user.permissions.map((_) => _._id), ...userRoles.map((_) => _.permissions).flat()];
|
||||
// const userRoles = user.roles.map((_) => _._id);
|
||||
// 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(_));
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -58,7 +58,7 @@ const deleteSubLevelTreeFromRoot = async (root_sub_level_id) => {
|
||||
const addSublevelToParent = async (payload, parent_id, parentIsLevel) => {
|
||||
if (parentIsLevel) {
|
||||
// add sublevel to parent
|
||||
const parentData = await Sublevel.findById(parent_id);
|
||||
const parentData = await Level.findById(parent_id);
|
||||
parentData.sub_levels.push(payload);
|
||||
return await parentData.save();
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const Warehouse = require("../models/Warehouse");
|
||||
const mongoose = require("mongoose");
|
||||
const { S3 } = require("./../config/aws");
|
||||
const Inventory = require("../models/Inventory");
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
@@ -19,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);
|
||||
@@ -29,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();
|
||||
@@ -49,34 +59,22 @@ module.exports = {
|
||||
res.status(404).send({ success: false, message: "not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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
|
||||
*/
|
||||
@@ -88,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;
|
||||
}
|
||||
@@ -106,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);
|
||||
@@ -119,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);
|
||||
|
||||
@@ -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,16 +21,11 @@ router.get("/:id/zones", controller.getWarehouseZonesByID);
|
||||
/**
|
||||
* @route /warehouse/
|
||||
*/
|
||||
router.post("/", controller.createWarehouse);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const mongoose = require("mongoose");
|
||||
const { InventoryTypes, WarehouseScopes } = require("./../config/constants");
|
||||
|
||||
const schema = new mongoose.Schema(
|
||||
{
|
||||
@@ -8,49 +7,47 @@ const schema = new mongoose.Schema(
|
||||
required: true,
|
||||
trim: true,
|
||||
},
|
||||
type: {
|
||||
icon_slug: {
|
||||
type: String,
|
||||
trim: true,
|
||||
required: true,
|
||||
// enum: []
|
||||
},
|
||||
widgetName: {
|
||||
type: String,
|
||||
required: true,
|
||||
trim: true,
|
||||
enum: InventoryTypes,
|
||||
},
|
||||
policies: {
|
||||
orderTracking: {
|
||||
type: Object, // Create a different model and reference it here once more details available
|
||||
},
|
||||
alerting: {
|
||||
lowestStockLevel: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
highestStockLevel: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
alertStockLevel: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
reOrderLevel: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
replenishment: {
|
||||
type: Boolean, // Create a different model and reference it here once more details available
|
||||
},
|
||||
inventory_process: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
// preferredLocations: [
|
||||
// {
|
||||
// id: {
|
||||
// type: mongoose.Schema.Types.ObjectId,
|
||||
// refPath: "type",
|
||||
// },
|
||||
// type: {
|
||||
// type: String,
|
||||
// enum: WarehouseScopes,
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
preferredLocations: {
|
||||
type: Object, // Create a different model and reference it here once more details available
|
||||
},
|
||||
preferredLocations: [
|
||||
{
|
||||
id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
refPath: "type",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: WarehouseScopes,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
const mongoose = require("mongoose");
|
||||
const { CustomAttributeTypes } = require("./../config/constants");
|
||||
|
||||
const ItemImage = new mongoose.Schema({
|
||||
url: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
});
|
||||
|
||||
const schema = new mongoose.Schema(
|
||||
{
|
||||
commonName: {
|
||||
@@ -13,6 +20,7 @@ const schema = new mongoose.Schema(
|
||||
required: true,
|
||||
trim: true,
|
||||
},
|
||||
images: [ItemImage],
|
||||
description: {
|
||||
type: String,
|
||||
required: true,
|
||||
@@ -93,6 +101,9 @@ const schema = new mongoose.Schema(
|
||||
alertStockLevelCount: {
|
||||
type: Number,
|
||||
},
|
||||
reorderStockLevelCount: {
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -17,6 +17,13 @@ const schema = new mongoose.Schema(
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
},
|
||||
usageReason: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
job: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
@@ -35,7 +42,7 @@ const PutItemTransaction = ItemTransaction.discriminator(
|
||||
},
|
||||
subLevel: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Sublevel"
|
||||
ref: "Sublevel",
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -61,13 +68,8 @@ const ReserveItemTransaction = ItemTransaction.discriminator(
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
job: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
required: true,
|
||||
},
|
||||
pickupDate: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -77,7 +79,6 @@ const CheckInItemTransaction = ItemTransaction.discriminator(
|
||||
new mongoose.Schema({
|
||||
checkInMeterReading: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
hasIssue: {
|
||||
type: Boolean,
|
||||
@@ -95,15 +96,6 @@ const CheckOutItemTransaction = ItemTransaction.discriminator(
|
||||
new mongoose.Schema({
|
||||
checkOutMeterReading: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
job: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
required: true,
|
||||
},
|
||||
usageReason: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const mongoose = require("mongoose");
|
||||
const { isEmail } = require("validator");
|
||||
const bcrypt = require("bcrypt");
|
||||
const { UserActions, WarehouseScopes, InventoryScopes, AllUIModules } = require("./../config/constants");
|
||||
|
||||
const schema = new mongoose.Schema(
|
||||
{
|
||||
@@ -36,22 +37,59 @@ const schema = new mongoose.Schema(
|
||||
passwordResetToken: {
|
||||
type: String,
|
||||
},
|
||||
image_url: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
roles: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "UserRole",
|
||||
},
|
||||
],
|
||||
permissions: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "UserPermission",
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
inventoryScopes: [
|
||||
{
|
||||
id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
refPath: "type",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: InventoryScopes,
|
||||
},
|
||||
},
|
||||
],
|
||||
warehouseScopes: [
|
||||
{
|
||||
id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
refPath: "type",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: WarehouseScopes,
|
||||
},
|
||||
},
|
||||
],
|
||||
allowedUIModules: [
|
||||
{
|
||||
type: String,
|
||||
enum: AllUIModules,
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: String,
|
||||
required: true,
|
||||
enum: UserActions,
|
||||
},
|
||||
],
|
||||
},
|
||||
createdBy: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -46,6 +46,11 @@ const schema = new mongoose.Schema(
|
||||
enum: UserActions,
|
||||
},
|
||||
],
|
||||
status: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const mongoose = require("mongoose");
|
||||
const { UserActions, WarehouseScopes, InventoryScopes, AllUIModules } = require("./../config/constants");
|
||||
|
||||
const schema = new mongoose.Schema(
|
||||
{
|
||||
@@ -7,12 +8,50 @@ const schema = new mongoose.Schema(
|
||||
required: true,
|
||||
trim: true,
|
||||
},
|
||||
permissions: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "UserPermission",
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
inventoryScopes: [
|
||||
{
|
||||
id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
refPath: "type",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: InventoryScopes,
|
||||
},
|
||||
},
|
||||
],
|
||||
warehouseScopes: [
|
||||
{
|
||||
id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
refPath: "type",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: WarehouseScopes,
|
||||
},
|
||||
},
|
||||
],
|
||||
allowedUIModules: [
|
||||
{
|
||||
type: String,
|
||||
enum: AllUIModules,
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: String,
|
||||
required: true,
|
||||
enum: UserActions,
|
||||
},
|
||||
],
|
||||
},
|
||||
status: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -16,6 +16,10 @@ const schema = new mongoose.Schema(
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
image_url: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
company_id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Company",
|
||||
@@ -26,6 +30,12 @@ const schema = new mongoose.Schema(
|
||||
ref: "Zone",
|
||||
},
|
||||
],
|
||||
preferredInventories: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Inventory",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -14,7 +14,7 @@ const schema = new mongoose.Schema(
|
||||
inventory: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Inventory",
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -22,6 +22,8 @@ const schema = new mongoose.Schema(
|
||||
}
|
||||
);
|
||||
|
||||
schema.index({ name: 1, parent: 1, inventory: 1 }, { unique: true });
|
||||
|
||||
const WidgetFamily = mongoose.model("WidgetFamily", schema);
|
||||
|
||||
module.exports = WidgetFamily;
|
||||
|
||||
Reference in New Issue
Block a user