squareData.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. const Owner = require("../models/owner.js");
  2. const Merchant = require("../models/merchant.js");
  3. const Recipe = require("../models/recipe.js");
  4. const helper = require("./helper.js");
  5. const axios = require("axios");
  6. const bcrypt = require("bcryptjs");
  7. module.exports = {
  8. /*POST - Redirects user to Square OAuth and saves input data
  9. req.body = {
  10. name: String,
  11. email: String,
  12. password: String,
  13. confirmPassword: String
  14. }
  15. */
  16. redirect: async function(req, res){
  17. if(req.body.password !== req.body.confirmPassword){
  18. req.session.error = "YOUR PASSWORDS DO NOT MATCH";
  19. return res.redirect("/");
  20. }
  21. let email = req.body.email.toLowerCase();
  22. let potentialOwner = await Owner.findOne({email: email})
  23. if(potentialOwner !== null){
  24. req.session.error = "USER WITH THIS EMAIL ADDRESS ALREADY EXISTS";
  25. return res.redirect("/login");
  26. }
  27. let expirationDate = new Date();
  28. expirationDate.setDate(expirationDate.getDate() + 90);
  29. let salt = bcrypt.genSaltSync(10);
  30. let hash = bcrypt.hashSync(req.body.password, salt);
  31. let owner = new Owner({
  32. email: email,
  33. password: hash,
  34. square: {},
  35. createdAt: new Date(),
  36. status: ["unverified"],
  37. session: {
  38. sessionId: helper.generateId(25),
  39. expiration: expirationDate
  40. },
  41. merchants: []
  42. });
  43. let merchant = new Merchant({
  44. owner: owner._id,
  45. name: req.body.name,
  46. pos: "square",
  47. inventory: [],
  48. recipes: [],
  49. square: {},
  50. createdAt: new Date()
  51. });
  52. owner.merchants.push(merchant._id);
  53. Promise.all([owner.save(), merchant.save()])
  54. .then((response)=>{
  55. req.session.owner = response[0].session.sessionId;
  56. return res.redirect(`${process.env.SQUARE_ADDRESS}/oauth2/authorize?client_id=${process.env.SUBLINE_SQUARE_APPID}&scope=INVENTORY_READ+ITEMS_READ+MERCHANT_PROFILE_READ+ORDERS_READ+PAYMENTS_READ`);
  57. })
  58. .catch((err)=>{
  59. res.session.error = "ERROR: UNABLE TO CREATE NEW USER";
  60. return res.redirect("/");
  61. });
  62. },
  63. //GET: Gathers all data from square to create our merchant
  64. //Redirects to the dashboard
  65. createMerchant: function(req, res){
  66. let code = req.url.slice(req.url.indexOf("code=") + 5, req.url.indexOf("&"));
  67. let url = `${process.env.SQUARE_ADDRESS}/oauth2/token`;
  68. let data = {
  69. client_id: process.env.SUBLINE_SQUARE_APPID,
  70. client_secret: process.env.SUBLINE_SQUARE_APPSECRET,
  71. grant_type: "authorization_code",
  72. code: code,
  73. }
  74. let merchant = {};
  75. let owner = Owner.findOne({"session.sessionId": req.session.owner}).populate("merchants");
  76. let squareMerchant = axios.post(url, data);
  77. Promise.all([owner, squareMerchant])
  78. .then((response)=>{
  79. if(response[0] === null) throw "ERROR: UNABLE TO CREATE ACCOUNT";
  80. owner = response[0];
  81. merchant = response[0].merchants[0];
  82. owner.square = {
  83. id: response[1].data.merchant_id,
  84. expires: new Date(response[1].data.expires_at),
  85. refreshToken: response[1].data.refresh_token,
  86. accessToken: response[1].data.access_token
  87. };
  88. return axios.get(`${process.env.SQUARE_ADDRESS}/v2/merchants/${owner.square.id}`, {
  89. headers: {Authorization: `Bearer ${owner.square.accessToken}`}
  90. });
  91. })
  92. .then((response)=>{
  93. merchant.locationId = response.data.merchant.main_location_id;
  94. let items = axios.post(`${process.env.SQUARE_ADDRESS}/v2/catalog/search`, {
  95. object_types: ["ITEM"]
  96. }, {
  97. headers: {
  98. Authorization: `Bearer ${owner.square.accessToken}`
  99. }
  100. });
  101. let location = axios.get(`${process.env.SQUARE_ADDRESS}/v2/locations/${merchant.locationId}`, {
  102. headers: {
  103. Authorization: `Bearer ${owner.square.accessToken}`
  104. }
  105. });
  106. return Promise.all([items, location]);
  107. })
  108. .then((response)=>{
  109. if(owner.email === response[1].data.location.business_email) merchant.status = [];
  110. let recipes = helper.createRecipesFromSquare(response[0].data.objects, merchant._id);
  111. merchant.recipes = recipes;
  112. return Promise.all([Recipe.create(recipes), owner.save(), merchant.save()]);
  113. })
  114. .then((response)=>{
  115. req.session.owner = response[1].session.sessionId;
  116. req.session.merchant = merchant._id;
  117. res.redirect("/dashboard");
  118. helper.getAllMerchantTransactions(merchant, owner.square.accessToken);
  119. })
  120. .catch((err)=>{
  121. if(typeof(err) === "string"){
  122. req.session.error = err;
  123. }else if(err.name === "ValidationError"){
  124. req.session.error = err.errors[Object.keys(err.errors)[0]].properties.message;
  125. }else{
  126. req.session.error = "ERROR: UNABLE TO CREATE NEW USER";
  127. }
  128. Merchant.deleteOne({_id: merchant._id});
  129. Owner.deleteOne({_id: owner._id});
  130. return res.redirect("/");
  131. });
  132. },
  133. updateRecipes: function(req, res){
  134. let merchantRecipes = [];
  135. let newRecipes = [];
  136. let populate = res.locals.merchant.populate("recipes").execPopulate();
  137. let squareRecipes = axios.post(`${process.env.SQUARE_ADDRESS}/v2/catalog/search-catalog-items`, {
  138. enabled_location_ids: [res.locals.merchant.locationId]
  139. }, {
  140. headers: {
  141. Authorization: `Bearer ${res.locals.owner.square.accessToken}`
  142. }
  143. });
  144. Promise.all([populate, squareRecipes])
  145. .then((response)=>{
  146. merchantRecipes = res.locals.merchant.recipes.slice();
  147. for(let i = 0; i < response[1].data.items.length; i++){
  148. let itemData = response[1].data.items[i].item_data;
  149. for(let j = 0; j < itemData.variations.length; j++){
  150. let isFound = false;
  151. for(let k = 0; k < merchantRecipes.length; k++){
  152. if(itemData.variations[j].id === merchantRecipes[k].posId){
  153. merchantRecipes.splice(k, 1);
  154. k--;
  155. isFound = true;
  156. break;
  157. }
  158. }
  159. if(!isFound){
  160. let priceMoney = itemData.variations[j].item_variation_data.price_money;
  161. let price = (priceMoney === undefined) ? 0 : priceMoney.amount;
  162. let newRecipe = new Recipe({
  163. posId: itemData.variations[j].id,
  164. merchant: res.locals.merchant._id,
  165. name: "",
  166. price: price,
  167. ingredients: []
  168. });
  169. if(itemData.variations.length > 1){
  170. newRecipe.name = `${itemData.name} '${itemData.variations[j].item_variation_data.name}'`;
  171. }else{
  172. newRecipe.name = itemData.name;
  173. }
  174. newRecipes.push(newRecipe);
  175. res.locals.merchant.recipes.push(newRecipe);
  176. }
  177. }
  178. }
  179. let ids = [];
  180. for(let i = 0; i < merchantRecipes.length; i++){
  181. ids.push(merchantRecipes[i]._id);
  182. for(let j = 0; j < res.locals.merchant.recipes.length; j++){
  183. if(merchantRecipes[i]._id.toString() === res.locals.merchant.recipes[j]._id.toString()){
  184. res.locals.merchant.recipes.splice(j, 1);
  185. j--;
  186. break;
  187. }
  188. }
  189. }
  190. if(newRecipes.length > 0) Recipe.create(newRecipes);
  191. if(merchantRecipes.length > 0) Recipe.deleteMany({_id: {$in: ids}});
  192. return res.locals.merchant.save();
  193. })
  194. .then((merchant)=>{
  195. return res.json({new: newRecipes, removed: merchantRecipes});
  196. })
  197. .catch((err)=>{
  198. if(typeof(err) === "string") return res.json(err);
  199. if(err.name === "ValidationError") return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
  200. return res.json("ERROR: UNABLE TO RETRIEVE RECIPE DATA FROM SQUARE");
  201. });
  202. },
  203. /*
  204. GET: add another merchant to an owner with another square location
  205. response = [{
  206. name: String,
  207. id: String
  208. }]
  209. */
  210. getLocations: function(req, res){
  211. let ownerLocation = res.locals.owner.populate("merchants", "locationId").execPopulate();
  212. let locations = axios.get(`${process.env.SQUARE_ADDRESS}/v2/locations`, {
  213. headers: {
  214. "Authorization": `Bearer ${res.locals.owner.square.accessToken}`,
  215. "Content-Type": "application/json"
  216. }
  217. });
  218. Promise.all([ownerLocation, locations])
  219. .then((response)=>{
  220. let checkLocations = [];
  221. let locations = [];
  222. for(let i = 0; i < res.locals.owner.merchants.length; i++){
  223. checkLocations.push(res.locals.owner.merchants[i].locationId);
  224. }
  225. for(let i = 0; i < response[1].data.locations.length; i++){
  226. if(checkLocations.includes(response[1].data.locations[i].id) === false){
  227. locations.push({
  228. name: response[1].data.locations[i].name,
  229. id: response[1].data.locations[i].id
  230. });
  231. }
  232. }
  233. return res.json(locations);
  234. })
  235. .catch((err)=>{
  236. return res.json("ERROR: UNABLE TO RETRIEVE LOCATION DATA FROM SQUARE");
  237. });
  238. },
  239. /*
  240. GET: create new merchant from square location and add to owner
  241. req.params.location = location.id
  242. response = [Owner, Merchant]
  243. */
  244. addMerchant: function(req, res){
  245. let merchant = new Merchant({
  246. owner: res.locals.owner._id,
  247. pos: "square",
  248. locationId: req.params.location,
  249. createdAt: new Date(),
  250. inventory: [],
  251. recipes: []
  252. });
  253. res.locals.owner.merchants.push(merchant._id);
  254. let location = axios.get(`${process.env.SQUARE_ADDRESS}/v2/locations/${req.params.location}`, {
  255. headers: {
  256. "Authorization": `Bearer ${res.locals.owner.square.accessToken}`,
  257. "Content-Type": "application/json"
  258. }
  259. });
  260. let recipes = axios.post(`${process.env.SQUARE_ADDRESS}/v2/catalog/search`, {
  261. object_types: ["ITEM"]
  262. }, {
  263. headers: {
  264. "Authorization": `Bearer ${res.locals.owner.square.accessToken}`,
  265. "Content-Type": "application/json"
  266. }
  267. });
  268. Promise.all([location, recipes])
  269. .then((response)=>{
  270. merchant.name = response[0].data.location.name;
  271. let recipes = helper.createRecipesFromSquare(response[1].data.objects, merchant._id);
  272. merchant.recipes = recipes;
  273. let populateOwner = res.locals.owner.populate("merchants", "name");
  274. return Promise.all([Recipe.create(recipes), res.locals.owner.save(), merchant.save(), populateOwner]);
  275. })
  276. .then((response)=>{
  277. req.session.merchant = merchant._id;
  278. res.json([{
  279. _id: res.locals.owner._id,
  280. email: res.locals.owner.email,
  281. merchants: res.locals.owner.merchants
  282. }, merchant]);
  283. helper.getAllMerchantTransactions(merchant, res.locals.owner.square.accessToken);
  284. })
  285. .catch((err)=>{
  286. if(err.name === "ValidationError") return req.session.err = err.errors[Object.keys(err.errors)[0]].properties.message;
  287. return res.json("ERROR: UNABLE TO CREATE NEW MERCHANT");
  288. });
  289. }
  290. }