merchantData.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. const Owner = require("../models/owner.js");
  2. const Merchant = require("../models/merchant.js");
  3. const InventoryAdjustment = require("../models/inventoryAdjustment");
  4. const Transaction = require("../models/transaction.js");
  5. const helper = require("./helper.js");
  6. const verifyEmail = require("../emails/verifyEmail.js");
  7. const bcrypt = require("bcryptjs");
  8. const mailgun = require("mailgun-js")({apiKey: process.env.MG_SUBLINE_APIKEY, domain: "mail.thesubline.net"});
  9. const ObjectId = require("mongoose").Types.ObjectId;
  10. module.exports = {
  11. /*
  12. GET: gets a merchant to send back to its owner
  13. req.params.id = String (merchant id)
  14. response = [Owner, Merchant, [Transaction]];
  15. */
  16. getMerchant: function(req, res){
  17. let owner = Owner.findOne({"session.sessionId": req.session.owner}).populate("merchants", "name");
  18. let merchant = Merchant.findOne({_id: req.params.id}).populate("inventory.ingredient").populate("recipes");
  19. let now = new Date();
  20. let then = new Date(now.getFullYear(), now.getMonth() - 1, 1);
  21. let transactions = Transaction.aggregate([
  22. {$match: {
  23. merchant: ObjectId(req.params.id),
  24. date: {$gte: then}
  25. }},
  26. {$sort: {date: -1}},
  27. {$project: {
  28. date: 1,
  29. recipes: 1
  30. }}
  31. ]);
  32. Promise.all([owner, merchant, transactions])
  33. .then((response)=>{
  34. if(response[0] === null || response[1] === null) throw "unfound";
  35. if(response[1].owner.toString() !== response[0]._id.toString()) throw "permissions";
  36. let responseOwner = {
  37. _id: response[0]._id,
  38. email: response[0].email,
  39. merchants: response[0].merchants,
  40. name: response[0].name
  41. };
  42. for(let i = 0; i < responseOwner.merchants.length; i++){
  43. if(response[1]._id.toString() === responseOwner.merchants[i]._id.toString()){
  44. responseOwner.merchants.splice(i, 1);
  45. break;
  46. }
  47. }
  48. response[1].owner = undefined;
  49. response[1].createdAt = undefined;
  50. req.session.merchant = response[1]._id;
  51. return res.json([responseOwner, response[1], response[2]]);
  52. })
  53. .catch((err)=>{
  54. if(err === "unfound") return res.json("UNABLE TO FIND THAT MERCHANT");
  55. if(err === "permissions") return res.json("YOU DO NOT HAVE PERMISSION TO DO THAT");
  56. return res.json("ERROR: UNABLE TO RETRIEVE DATA");
  57. });
  58. },
  59. /*
  60. POST - Create a new merchant with no POS system
  61. req.body = {
  62. name: retaurant name,
  63. email: registration email,
  64. password: password,
  65. confirmPassword: confirmation password
  66. owner: String (id of the owner, new owner of undefined)
  67. }
  68. Redirects to /dashboard
  69. */
  70. createMerchantNone: async function(req, res){
  71. if(req.body.password.length < 10){
  72. req.session.error = "PASSWORD MUST CONTAIN AT LEAST 10 CHARACTERS";
  73. return res.redirect("/register");
  74. }
  75. if(req.body.password !== req.body.confirmPassword){
  76. req.session.error = "PASSWORDS DO NOT MATCH";
  77. return res.redirect("/register");
  78. }
  79. let email = req.body.email.toLowerCase();
  80. const merchantFind = await Merchant.findOne({email: email});
  81. if(merchantFind !== null){
  82. req.session.error = "USER WITH THIS EMAIL ADDRESS ALREADY EXISTS";
  83. return res.redirect("/login");
  84. }
  85. let salt = bcrypt.genSaltSync(10);
  86. let hash = bcrypt.hashSync(req.body.password, salt);
  87. let expirationDate = new Date();
  88. expirationDate.setDate(expirationDate.getDate() + 90);
  89. let owner = new Owner({
  90. email: email,
  91. name: req.body.name,
  92. password: hash,
  93. createdAt: new Date(),
  94. status: ["unverified"],
  95. session: {
  96. sessionId: helper.generateId(25),
  97. expiration: expirationDate
  98. },
  99. merchants: []
  100. });
  101. let merchant = new Merchant({
  102. owner: owner._id,
  103. name: req.body.name,
  104. pos: "none",
  105. createdAt: Date.now(),
  106. inventory: [],
  107. recipes: []
  108. });
  109. owner.merchants.push(merchant._id);
  110. Promise.all([owner.save(), merchant.save()])
  111. .then((response)=>{
  112. return res.redirect(`/verify/email/${response[0]._id}`);
  113. })
  114. .catch((err)=>{
  115. if(typeof(err) === "string"){
  116. req.session.error = err;
  117. }else if(err.name === "ValidationError"){
  118. req.session.error = err.errors[Object.keys(err.errors)[0]].properties.message;
  119. }else{
  120. req.session.error = "ERROR: UNABLE TO CREATE ACCOUNT AT THIS TIME";
  121. }
  122. return res.redirect("/");
  123. });
  124. },
  125. /*
  126. POST: create new merchant for existing owner
  127. req.body = {
  128. name: String
  129. }
  130. response = [Owner, Merchant]
  131. */
  132. addMerchantNone: function(req, res){
  133. let merchant = new Merchant({
  134. owner: res.locals.owner._id,
  135. name: req.body.name,
  136. pos: "none",
  137. createdAt: new Date(),
  138. inventory: [],
  139. recipes: []
  140. });
  141. res.locals.owner.merchants.push(merchant._id);
  142. let populate = res.locals.owner.populate("merchants", "name").execPopulate();
  143. Promise.all([res.locals.owner.save(), merchant.save(), populate])
  144. .then((response)=>{
  145. let owner = {
  146. _id: response[0]._id,
  147. email: response[0].email,
  148. merchants: response[0].merchants,
  149. name: response[0].name
  150. }
  151. req.session.merchant = response[1]._id;
  152. return res.json([owner, response[1]]);
  153. })
  154. .catch((err)=>{
  155. if(err.name === "ValidationError"){
  156. return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
  157. }
  158. return res.json("ERROR: UNABLE TO CREATE NEW MERCHANT");
  159. });
  160. },
  161. /*
  162. POST - Update the quantity for a merchant inventory item
  163. req.body = [{
  164. id: id of ingredient to update,
  165. quantity: change in quantity
  166. }]
  167. */
  168. updateIngredientQuantities: function(req, res){
  169. let adjustments = [];
  170. let changedIngredients = [];
  171. res.locals.merchant
  172. .populate("inventory.ingredient")
  173. .execPopulate()
  174. .then((merchant)=>{
  175. for(let i = 0; i < req.body.length; i++){
  176. let updateIngredient;
  177. for(let j = 0; j < merchant.inventory.length; j++){
  178. if(merchant.inventory[j].ingredient._id.toString() === req.body[i].id){
  179. updateIngredient = merchant.inventory[j];
  180. break;
  181. }
  182. }
  183. adjustments.push(new InventoryAdjustment({
  184. date: Date.now(),
  185. merchant: req.session.owner,
  186. ingredient: req.body[i].id,
  187. quantity: req.body[i].quantity - updateIngredient.quantity,
  188. }));
  189. updateIngredient.quantity = helper.convertQuantityToBaseUnit(req.body[i].quantity, updateIngredient.defaultUnit);
  190. changedIngredients.push(updateIngredient);
  191. }
  192. return merchant.save();
  193. })
  194. .then((newMerchant)=>{
  195. res.json(changedIngredients);
  196. InventoryAdjustment.create(adjustments).catch(()=>{});
  197. return;
  198. })
  199. .catch((err)=>{
  200. if(typeof(err) === "string"){
  201. return res.json(err);
  202. }
  203. if(err.name === "ValidationError"){
  204. return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
  205. }
  206. return res.json("ERROR: UNABLE TO UPDATE DATA");
  207. });
  208. },
  209. /*
  210. POST - Changes the users password
  211. req.body = {
  212. pass: new password,
  213. confirmPass: new password confirmation,
  214. hash: hashed version of old password
  215. }
  216. */
  217. updatePassword: function(req, res){
  218. Merchant.findOne({password: req.body.hash})
  219. .then((merchant)=>{
  220. if(merchant){
  221. if(req.body.pass.length < 10){
  222. throw "PASSWORD MUST CONTAIN AT LEAST 10 CHARACTERS";
  223. }
  224. if(req.body.pass !== req.body.confirmPass){
  225. throw "PASSWORDS DO NOT MATCH";
  226. }
  227. let salt = bcrypt.genSaltSync(10);
  228. let hash = bcrypt.hashSync(req.body.pass, salt);
  229. merchant.password = hash;
  230. return merchant.save();
  231. }else{
  232. req.session.error = "ERROR: UNABLE TO RETRIEVE DATA";
  233. return res.redirect("/");
  234. }
  235. })
  236. .then((merchant)=>{
  237. req.session.success = "PASSWORD SUCCESSFULLY RESET. PLEASE LOG IN";
  238. return res.redirect("/login");
  239. })
  240. .catch((err)=>{
  241. if(typeof(err) === "string"){
  242. return res.json(err);
  243. }
  244. if(err.name === "ValidationError"){
  245. return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
  246. }
  247. return res.json("ERROR: UNABLE TO UPDATE YOUR PASSWORD");
  248. });
  249. },
  250. /*
  251. PUT: Update merchant data
  252. req.body = {
  253. email: String
  254. name: String
  255. },
  256. response = {
  257. email: String
  258. name: String
  259. }
  260. */
  261. updateData: async function(req, res){
  262. if(req.body.email !== res.locals.owner.email){
  263. let ownerCheck = await Owner.findOne({email: req.body.email});
  264. if(ownerCheck !== null) return res.json("USER WITH THIS EMAIL ADDRESS ALREADY EXISTS");
  265. res.locals.owner.email = req.body.email.toLowerCase();
  266. res.locals.owner.status.push("unverified");
  267. const mailgunData = {
  268. from: "The Subline <clientsupport@thesubline.net>",
  269. to: res.locals.owner.email,
  270. subject: "Email Verification",
  271. html: verifyEmail({
  272. name: res.locals.owner.name,
  273. link: `${process.env.SITE}/verify/${res.locals.owner._id}/${res.locals.owner.session.sessionId}`
  274. })
  275. };
  276. mailgun.messages().send(mailgunData, (err, body)=>{});
  277. }
  278. res.locals.owner.name = req.body.name;
  279. res.locals.owner.save()
  280. .then((owner)=>{
  281. return res.json({
  282. email: res.locals.owner.email,
  283. name: res.locals.owner.name
  284. });
  285. })
  286. .catch((err)=>{
  287. if(err.name === "ValidationError") return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
  288. return res.json("ERROR: UNABLE TO UPDATE DATA");
  289. });
  290. },
  291. /*
  292. PUT: Update merchant password with current password
  293. req.body = {
  294. current: String (current merchant password),
  295. new: String (new password),
  296. confirm: String (new password again for confirmation)
  297. }
  298. response = {redirect: String (link to redirect to)}
  299. */
  300. changePassword: function(req, res){
  301. if(req.body.new !== req.body.confirm) return res.json("PASSWORDS DO NOT MATCH");
  302. bcrypt.compare(req.body.current, res.locals.owner.password, (err, result)=>{
  303. if(result === true){
  304. let salt = bcrypt.genSaltSync(10);
  305. let hash = bcrypt.hashSync(req.body.new, salt);
  306. res.locals.owner.password = hash;
  307. let newExpiration = new Date();
  308. newExpiration.setDate(newExpiration.getDate() + 90);
  309. res.locals.owner.session.sessionId = helper.generateId(25);
  310. res.locals.owner.session.expiration = newExpiration;
  311. res.locals.owner.save()
  312. .then((owner)=>{
  313. req.session.owner = undefined;
  314. req.session.merchant = undefined;
  315. req.session.success = "PASSWORD RESET. PLEASE LOG IN AGAIN.";
  316. return res.json({redirect: `http://${process.env.SITE}/login`});
  317. })
  318. .catch((err)=>{
  319. return res.json("ERROR: UNABLE TO UPDATE PASSWORD");
  320. });
  321. }else{
  322. return res.json("INCORRECT PASSWORD");
  323. }
  324. });
  325. },
  326. /*
  327. DELETE: remove a merchant from its owner
  328. response = [Owner, Merchant (the next), [Transaction]]
  329. */
  330. deleteMerchant: function(req, res){
  331. if(res.locals.owner.merchants.length === 1) throw "one";
  332. for(let i = 0; i < res.locals.owner.merchants.length; i++){
  333. if(res.locals.owner.merchants[i].toString() === res.locals.merchant._id.toString()){
  334. res.locals.owner.merchants.splice(i, 1);
  335. break;
  336. }
  337. }
  338. res.locals.merchant.removed = true;
  339. let now = new Date();
  340. let then = new Date(now.getFullYear(), now.getMonth() - 1, 1);
  341. let transactions = Transaction.aggregate([
  342. {$match: {
  343. merchant: ObjectId(res.locals.owner.merchants[0]._id),
  344. date: {$gte: then}
  345. }},
  346. {$sort: {date: -1}},
  347. {$project: {
  348. date: 1,
  349. recipes: 1
  350. }}
  351. ]);
  352. Promise.all([
  353. Merchant.findOne({_id: res.locals.owner.merchants[0]._id}).populate("inventory.ingredient").populate("recipes"),
  354. res.locals.owner.save(),
  355. res.locals.merchant.save(),
  356. res.locals.owner.populate("merchants", "name").execPopulate(),
  357. transactions
  358. ])
  359. .then((response)=>{
  360. let responseOwner = {
  361. _id: res.locals.owner._id,
  362. email: res.locals.owner.email,
  363. merchants: res.locals.owner.merchants.slice(1),
  364. name: res.locals.owner.name
  365. };
  366. response[0].owner = undefined;
  367. response[0].createdAt = undefined;
  368. response[0].locationId = undefined;
  369. req.session.merchant = response[0]._id;
  370. return res.json([responseOwner, response[0], response[4]]);
  371. })
  372. .catch((err)=>{
  373. if(err === "one") return res.json("YOU CANNOT DELETE YOUR ONLY MERCHANT");
  374. return res.json("ERROR: UNABLE TO DELETE THE MERCHANT");
  375. });
  376. }
  377. }