merchantData.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. const axios = require("axios");
  2. const bcrypt = require("bcryptjs");
  3. const Merchant = require("../models/merchant");
  4. const Recipe = require("../models/recipe");
  5. const InventoryAdjustment = require("../models/inventoryAdjustment");
  6. const RecipeChange = require("../models/recipeChange");
  7. const token = "b48068eb-411a-918e-ea64-52007147e42c";
  8. module.exports = {
  9. //GET - Checks clover for new or deleted recipes
  10. //Returns:
  11. // merchant: Full merchant (recipe ingredients populated)
  12. // count: Number of new recipes
  13. updateRecipes: function(req, res){
  14. if(!req.session.user){
  15. req.session.error = "Must be logged in to do that";
  16. return res.redirect("/");
  17. }
  18. Merchant.findOne({_id: req.session.user})
  19. .populate("recipes")
  20. .then((merchant)=>{
  21. axios.get(`https://apisandbox.dev.clover.com/v3/merchants/${merchant.posId}/items?access_token=${token}`)
  22. .then((result)=>{
  23. let deletedRecipes = merchant.recipes.slice();
  24. for(let i = 0; i < result.data.elements.length; i++){
  25. for(let j = 0; j < deletedRecipes.length; j++){
  26. if(result.data.elements[i].id === deletedRecipes[j].posId){
  27. result.data.elements.splice(i, 1);
  28. deletedRecipes.splice(j, 1);
  29. i--;
  30. break;
  31. }
  32. }
  33. }
  34. for(let recipe of deletedRecipes){
  35. for(let i = 0; i < merchant.recipes.length; i++){
  36. if(recipe._id === merchant.recipes[i]._id){
  37. merchant.recipes.splice(i, 1);
  38. break;
  39. }
  40. }
  41. }
  42. let newRecipes = []
  43. for(let recipe of result.data.elements){
  44. let newRecipe = new Recipe({
  45. posId: recipe.id,
  46. merchant: merchant._id,
  47. name: recipe.name,
  48. ingredients: []
  49. });
  50. merchant.recipes.push(newRecipe);
  51. newRecipes.push(newRecipe);
  52. }
  53. Recipe.create(newRecipes)
  54. .catch((err)=>{
  55. return res.json("Error: unable to create recipes");
  56. });
  57. merchant.save()
  58. .then((newMerchant)=>{
  59. newMerchant.populate(["recipes.ingredients.ingredient", "inventory.ingredient"]).execPopulate()
  60. .then((newestMerchant)=>{
  61. merchant.password = undefined;
  62. return res.json(newestMerchant);
  63. })
  64. .catch((err)=>{
  65. return res.json("Error: unable to retrieve user data");
  66. });
  67. })
  68. .catch((err)=>{
  69. return res.json("Error: unable to retrieve user data");
  70. });
  71. })
  72. .catch((err)=>{
  73. return res.json("Error: unable to retrieve data from Clover");
  74. });
  75. })
  76. .catch((err)=>{
  77. return res.json("Error: unable to retrieve user data");
  78. });
  79. },
  80. //POST - Creates a Clover merchant from all entered data
  81. //Inputs:
  82. // req.body.data: All data from frontend in form of merchant model
  83. //Redirect to "/inventory"
  84. createMerchantClover: async function(req, res){
  85. let data = JSON.parse(req.body.data);
  86. data.email = data.email.toLowerCase();
  87. let merchant = await Merchant.findOne({email: data.email});
  88. if(merchant){
  89. req.session.error = "Email already in use";
  90. return res.redirect("/merchant/new/clover");
  91. }
  92. if(data.password.length < 15 || data.password !== data.confirmPassword){
  93. req.session.error = "Passwords must match and contain at least 15 characters";
  94. return res.redirect("/");
  95. }
  96. axios.get(`https://apisandbox.dev.clover.com/v3/merchants/${req.session.merchantId}?access_token=${req.session.accessToken}`)
  97. .then((cloverMerchant)=>{
  98. req.session.posId = undefined;
  99. let salt = bcrypt.genSaltSync(10);
  100. let hash = bcrypt.hashSync(data.password, salt);
  101. let merchant = new Merchant({
  102. name: cloverMerchant.data.name,
  103. email: data.email,
  104. password: hash,
  105. pos: "clover",
  106. posId: cloverMerchant.data.id,
  107. posAccessToken: req.session.accessToken
  108. });
  109. req.session.merchantId = undefined;
  110. req.session.accessToken = undefined;
  111. for(let item of data.inventory){
  112. merchant.inventory.push({
  113. ingredient: item.ingredient.id,
  114. quantity: item.quantity
  115. });
  116. }
  117. for(let recipe of data.recipes){
  118. recipe.merchant = merchant._id;
  119. }
  120. Recipe.create(data.recipes)
  121. .then((recipes)=>{
  122. for(let recipe of recipes){
  123. merchant.recipes.push(recipe._id);
  124. }
  125. merchant.save()
  126. .then((merchant)=>{
  127. req.session.user = merchant._id;
  128. return res.redirect("/inventory");
  129. })
  130. .catch((err)=>{
  131. req.session.error = "Error: your data could not be saved";
  132. return res.redirect("/");
  133. });
  134. })
  135. .catch((err)=>{
  136. req.session.error = "Error: your recipes could not be created";
  137. return res.redirect("/");
  138. });
  139. })
  140. .catch((err)=>{
  141. req.session.error = "Error: Unable to retrieve your data from Clover";
  142. return res.redirect("/");
  143. });
  144. },
  145. //POST - Creates a non-pos merchant from all entered data
  146. //Inputs:
  147. // req.body.data: All data from frontend in form of merchant model
  148. //Redirects to "/inventory"
  149. createMerchantNone: async function(req, res){
  150. let data = JSON.parse(req.body.data);
  151. data.email = data.email.toLowerCase();
  152. let merchantExists = await Merchant.findOne({email: data.email});
  153. if(merchantExists){
  154. req.session.error = "Email already in use";
  155. return res.redirect("/merchant/new/none");
  156. }
  157. if(data.password.length < 15 || data.password !== data.confirmPassword){
  158. req.session.error = "Passwords must match and contain at least 15 characters";
  159. return res.redirect("/");
  160. }
  161. let salt = bcrypt.genSaltSync(10);
  162. let hash = bcrypt.hashSync(data.password, salt);
  163. let merchant = new Merchant({
  164. name: data.name,
  165. email: data.email,
  166. password: hash,
  167. pos: "none",
  168. accountStatus: {
  169. status: "valid",
  170. //working here
  171. }
  172. });
  173. for(let item of data.inventory){
  174. merchant.inventory.push({
  175. ingredient: item.ingredient.id,
  176. quantity: item.quantity
  177. });
  178. }
  179. for(let recipe of data.recipes){
  180. recipe.merchant = merchant._id;
  181. }
  182. Recipe.create(data.recipes)
  183. .then((recipes)=>{
  184. for(let recipe of recipes){
  185. merchant.recipes.push(recipe._id);
  186. }
  187. merchant.save()
  188. .then((merchant)=>{
  189. req.session.user = merchant._id;
  190. return res.redirect("/inventory");
  191. })
  192. .catch((err)=>{
  193. req.session.error = "Error: unable to save user data";
  194. return res.redirect("/");
  195. });
  196. })
  197. .catch((err)=>{
  198. req.session.error = "Error: unable to create recipes";
  199. return res.redirect("/");
  200. });
  201. },
  202. //POST - Adds an ingredient to merchant's inventory
  203. //Inputs:
  204. // req.body.ingredient: ingredient id
  205. // req.body.quantity: quantity for the ingredient
  206. //Returns:
  207. // ingredient: Newly added ingredient
  208. addMerchantIngredient: function(req, res){
  209. if(!req.session.user){
  210. req.session.error = "Must be logged in to do that";
  211. return res.redirect("/");
  212. }
  213. Merchant.findOne({_id: req.session.user})
  214. .then((merchant)=>{
  215. for(let item of merchant.inventory){
  216. if(item.ingredient.toString() === req.body.ingredient){
  217. return res.json("Ingredient is already in your inventory");
  218. }
  219. }
  220. merchant.inventory.push(req.body);
  221. merchant.save()
  222. .then((newMerchant)=>{
  223. newMerchant.populate("inventory.ingredient", (err)=>{
  224. if(err){
  225. return res.json("Warning: refresh page to view updates");
  226. }else{
  227. let newIngredient = newMerchant.inventory.find(i => i.ingredient._id.toString() === req.body.ingredient);
  228. return res.json(newIngredient);
  229. }
  230. });
  231. })
  232. .catch((err)=>{
  233. return res.json("Error: unable to save new ingredient");
  234. });
  235. })
  236. .catch((err)=>{
  237. return res.json("Error: unable to retrieve user data");
  238. });
  239. },
  240. //POST - Removes an ingredient from the merchant's inventory
  241. //Inputs:
  242. // ingredientId: id of ingredient to remove
  243. //Returns: Nothing
  244. removeMerchantIngredient: function(req, res){
  245. if(!req.session.user){
  246. req.session.error = "Must be logged in to do that";
  247. return res.redirect("/");
  248. }
  249. Merchant.findOne({_id: req.session.user})
  250. .then((merchant)=>{
  251. for(let i = 0; i < merchant.inventory.length; i++){
  252. if(req.body.ingredientId === merchant.inventory[i].ingredient._id.toString()){
  253. merchant.inventory.splice(i, 1);
  254. break;
  255. }
  256. }
  257. merchant.save()
  258. .then((merchant)=>{
  259. return res.json(req.body);
  260. })
  261. .catch((err)=>{
  262. return res.json("Error: unable to save user data");
  263. });
  264. })
  265. .catch((err)=>{
  266. return res.json("Error: unable to retrieve user data");
  267. });
  268. },
  269. //POST - Update the quantity for a merchant inventory item
  270. //Inputs:
  271. // req.body.ingredientId: Id of ingredient to update
  272. // req.body.quantityChange: Amount to change ingredient (not the new value)
  273. //Returns: Nothing
  274. updateMerchantIngredient: function(req, res){
  275. if(!req.session.user){
  276. req.session.error = "Must be logged in to do that";
  277. return res.redirect("/");
  278. }
  279. Merchant.findOne({_id: req.session.user})
  280. .then((merchant)=>{
  281. let updateIngredient = merchant.inventory.find(i => i.ingredient.toString() === req.body.ingredientId);
  282. updateIngredient.quantity = (updateIngredient.quantity + req.body.quantityChange).toFixed(2);
  283. merchant.save()
  284. .then((newMerchant)=>{
  285. res.json({});
  286. })
  287. .catch((err)=>{
  288. return res.json("Error: your data could not be saved");
  289. })
  290. })
  291. .catch((err)=>{
  292. return res.json("Error: your data could not be retrieved");
  293. });
  294. let invAdj = new InventoryAdjustment({
  295. date: Date.now(),
  296. merchant: req.session.user,
  297. ingredient: req.body.ingredientId,
  298. quantity: req.body.quantityChange
  299. });
  300. invAdj.save().catch((err)=>{});
  301. },
  302. //POST - Adds an ingredient to a recipe
  303. //Inputs:
  304. // req.body.recipeId: Id of recipe to change
  305. // req.body.item: Ingredient to add with a quantity
  306. //Returns:
  307. // recipe: Updated recipe with populated ingredients
  308. addRecipeIngredient: function(req, res){
  309. if(!req.session.user){
  310. req.session.error = "Must be logged in to do that";
  311. return res.redirect("/");
  312. }
  313. Recipe.findOne({_id: req.body.recipeId})
  314. .then((recipe)=>{
  315. recipe.ingredients.push({
  316. ingredient: req.body.item.ingredient,
  317. quantity: req.body.item.quantity
  318. });
  319. recipe.save()
  320. .then((recipe)=>{
  321. recipe.populate("ingredients.ingredient", (err)=>{
  322. if(err){
  323. return res.json("Error: could not retrieve ingredients. Please refresh page to see changes");
  324. }
  325. res.json(recipe);
  326. let rc = new RecipeChange({
  327. recipe: recipe,
  328. ingredient: req.body.item.ingredient,
  329. change: req.body.item.quantity
  330. });
  331. rc.save().catch((err)=>{});
  332. return;
  333. })
  334. })
  335. .catch((err)=>{
  336. return res.json("Error: unable to save recipe data");
  337. });
  338. })
  339. .catch((err)=>{
  340. return res.json("Error: unable to retrieve recipe data");
  341. });
  342. },
  343. //POST - Change quantity of a recipe's ingredient
  344. //Inputs:
  345. // req.body.recipeId: Id of recipe containing the ingredient
  346. // req.body.ingredient: The ingredient to update (_id and quantity)
  347. //Returns: Nothing
  348. updateRecipeIngredient: function(req, res){
  349. if(!req.session.user){
  350. req.session.error = "Must be logged in to do that";
  351. return res.redirect("/");
  352. }
  353. Recipe.findOne({_id: req.body.recipeId})
  354. .then((recipe)=>{
  355. for(let ingredient of recipe.ingredients){
  356. if(ingredient._id.toString() === req.body.ingredient._id){
  357. let change = Number(req.body.ingredient.quantity) - ingredient.quantity;
  358. ingredient.quantity = req.body.ingredient.quantity;
  359. recipe.save()
  360. .then((recipe)=>{
  361. res.json({});
  362. let rc = new RecipeChange({
  363. recipe: recipe,
  364. ingredient: ingredient.ingredient,
  365. change: change
  366. });
  367. rc.save().catch((err)=>{});
  368. return;
  369. })
  370. .catch((err)=>{
  371. return res.json("Error: Could not save recipe data");
  372. });
  373. }
  374. }
  375. })
  376. .catch((err)=>{
  377. return res.json("Error: unable to retrieve recipe data");
  378. });
  379. },
  380. //POST - Remove an ingredient from a recipe
  381. //Inputs:
  382. // req.body.ingredientId: Id of ingredient to be removed
  383. // req.body.recipeId: Id of recipe to remove ingredient from
  384. // req.body.quantity: quantity of recipe ingredient for storing
  385. //Returns: Nothing
  386. removeRecipeIngredient: function(req, res){
  387. if(!req.session.user){
  388. req.session.error = "Must be logged in to do that";
  389. return res.redirect("/");
  390. }
  391. Recipe.findOne({_id: req.body.recipeId})
  392. .then((recipe)=>{
  393. for(let i = 0; i < recipe.ingredients.length; i++){
  394. if(recipe.ingredients[i].ingredient._id.toString() === req.body.ingredientId){
  395. recipe.ingredients.splice(i, 1);
  396. break;
  397. }
  398. }
  399. recipe.save()
  400. .then((recipe)=>{
  401. res.json({});
  402. let rc = new RecipeChange({
  403. recipe: req.body.recipeId,
  404. ingredient: req.body.ingredientId,
  405. change: -req.body.quantity
  406. });
  407. rc.save().catch((err)=>{});
  408. return;
  409. })
  410. .catch((err)=>{
  411. return res.json("Error: unable to save recipe data");
  412. });
  413. })
  414. .catch((err)=>{
  415. return res.json("Error: unable to retrieve recipe data");
  416. });
  417. },
  418. //POST - Update merchant information
  419. //Inputs:
  420. // req.body.name: name update
  421. // req.body.email: email update
  422. //Returns: Nothing
  423. updateMerchant: function(req, res){
  424. if(!req.session.user){
  425. req.session.error = "Must be logged in to do that";
  426. return res.redirect("/");
  427. }
  428. Merchant.findOne({_id: req.session.user})
  429. .then((merchant)=>{
  430. merchant.name = req.body.name;
  431. merchant.email = req.body.email;
  432. merchant.save()
  433. .then((updatedMerchant)=>{
  434. return res.json({});
  435. })
  436. .catch((err)=>{
  437. return res.json("Error: unable to save merchant data");
  438. });
  439. })
  440. .catch((err)=>{
  441. return res.json("Error: unable to retrieve merchant data");
  442. });
  443. },
  444. //POST - Update merchant password
  445. //Inputs:
  446. // req.body.oldPass: current merchant password (supposedly)
  447. // req.body.newPass: replacement password
  448. //Returns: Nothing
  449. updatePassword: function(req, res){
  450. if(!req.session.user){
  451. req.session.error = "Must be logged in to do that";
  452. return res.redirect("/");
  453. }
  454. Merchant.findOne({_id: req.session.user})
  455. .then((merchant)=>{
  456. bcrypt.compare(req.body.oldPass, merchant.password, (err, result)=>{
  457. if(result){
  458. let salt = bcrypt.genSaltSync(10);
  459. let hash = bcrypt.hashSync(req.body.newPass, salt);
  460. merchant.password = hash;
  461. merchant.save()
  462. .then((updatedMerchant)=>{
  463. return res.json({});
  464. })
  465. .catch((err)=>{
  466. return res.json("Error: Unable to save new password");
  467. });
  468. }else{
  469. return res.json("Error: old password does not match current password");
  470. }
  471. });
  472. })
  473. .catch((err)=>{
  474. return res.json("Error: Unable to retrieve merchant data");
  475. });
  476. }
  477. }