squareData.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. const Merchant = require("../models/merchant.js");
  2. const Recipe = require("../models/recipe.js");
  3. const Transaction = require("../models/transaction.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: 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 expirationDate = new Date();
  22. expirationDate.setDate(expirationDate.getDate() + 90);
  23. let salt = bcrypt.genSaltSync(10);
  24. let hash = bcrypt.hashSync(req.body.password, salt);
  25. let merchant = new Merchant({
  26. name: req.body.name,
  27. email: req.body.email,
  28. password: hash,
  29. pos: "square",
  30. status: ["unverified"],
  31. inventory: [],
  32. recipes: [],
  33. square: {},
  34. createdAt: new Date(),
  35. session: {
  36. sessionId: helper.generateId(25),
  37. expiration: expirationDate
  38. }
  39. });
  40. merchant.save()
  41. .then((response)=>{
  42. req.session.user = merchant.session.sessionId;
  43. 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`);
  44. })
  45. .catch((err)=>{
  46. res.session.error = "ERROR: UNABLE TO CREATE NEW USER";
  47. return res.redirect("/");
  48. });
  49. },
  50. //GET: Used by square. This route is used for the authentication code
  51. //Redirects to either dashboard or new merchant creation
  52. // authorize: function(req, res){
  53. // const code = req.url.slice(req.url.indexOf("code=") + 5, req.url.indexOf("&"));
  54. // const url = `${process.env.SQUARE_ADDRESS}/oauth2/token`;
  55. // let data = {
  56. // client_id: process.env.SUBLINE_SQUARE_APPID,
  57. // client_secret: process.env.SUBLINE_SQUARE_APPSECRET,
  58. // grant_type: "authorization_code",
  59. // code: code
  60. // };
  61. // axios.post(url, data)
  62. // .then((response)=>{
  63. // data = response.data;
  64. // return Merchant.findOne({posId: data.merchant_id});
  65. // })
  66. // .then((merchant)=>{
  67. // if(merchant){
  68. // merchant.posAccessToken = data.access_token;
  69. // return merchant.save()
  70. // .then((merchant)=>{
  71. // req.session.user = merchant.session.sessionId;
  72. // return res.redirect("/dashboard");
  73. // })
  74. // .catch((err)=>{
  75. // req.session.error = "ERROR: UNABLE TO CREATE NEW USER";
  76. // return res.redirect("/");
  77. // })
  78. // }else{
  79. // req.session.merchantId = data.merchant_id;
  80. // req.session.accessToken = data.access_token;
  81. // return res.redirect("/merchant/create/square");
  82. // }
  83. // })
  84. // .catch((err)=>{
  85. // req.session.error = "ERROR: UNABLE TO RETRIEVE DATA FROM SQUARE";
  86. // return res.redirect("/");
  87. // });
  88. // },
  89. //GET: Gathers all data from square to create our merchant
  90. //Redirects to the dashboard
  91. createMerchant: function(req, res){
  92. let code = req.url.slice(req.url.indexOf("code=") + 5, req.url.indexOf("&"));
  93. let url = `${process.env.SQUARE_ADDRESS}/oauth2/token`;
  94. let data = {
  95. client_id: process.env.SUBLINE_SQUARE_APPID,
  96. client_secret: process.env.SUBLINE_SQUARE_APPSECRET,
  97. grant_type: "authorization_code",
  98. code: code,
  99. }
  100. let merchant = {};
  101. let localMerchant = Merchant.findOne({"session.sessionId": req.session.user});
  102. let squareMerchant = axios.post(url, data);
  103. Promise.all([localMerchant, squareMerchant])
  104. .then((response)=>{
  105. if(response[0] === null) throw "ERROR: UNABLE TO CREATE ACCOUNT";
  106. merchant = response[0];
  107. merchant.square = {
  108. id: response[1].data.merchant_id,
  109. expires: new Date(response[1].data.expires_at),
  110. refreshToken: response[1].data.refresh_token,
  111. accessToken: response[1].data.access_token
  112. };
  113. return axios.get(`${process.env.SQUARE_ADDRESS}/v2/merchants/${merchant.square.id}`, {
  114. headers: {Authorization: `Bearer ${merchant.square.accessToken}`}
  115. });
  116. })
  117. .then((response)=>{
  118. merchant.square.location = response.data.merchant.main_location_id;
  119. let items = axios.post(`${process.env.SQUARE_ADDRESS}/v2/catalog/search`, {
  120. object_types: ["ITEM"]
  121. }, {
  122. headers: {
  123. Authorization: `Bearer ${merchant.square.accessToken}`
  124. }
  125. });
  126. let location = axios.get(`${process.env.SQUARE_ADDRESS}/v2/locations/${merchant.square.location}`, {
  127. headers: {
  128. Authorization: `Bearer ${merchant.square.accessToken}`
  129. }
  130. });
  131. return Promise.all([items, location]);
  132. })
  133. .then((response)=>{
  134. if(merchant.email === response[1].data.location.business_email) merchant.status = [];
  135. let recipes = [];
  136. for(let i = 0; i < response[0].data.objects.length; i++){
  137. if(response[0].data.objects[i].item_data.variations.length > 1){
  138. for(let j = 0; j < response[0].data.objects[i].item_data.variations.length; j++){
  139. let item = response[0].data.objects[i].item_data.variations[j];
  140. let price = 0;
  141. if(item.item_variation_data.price_money !== undefined) price = item.item_variation_data.price_money.amount;
  142. let recipe = new Recipe({
  143. posId: item.id,
  144. merchant: merchant._id,
  145. name: `${response[0].data.objects[i].item_data.name} '${item.item_variation_data.name}'`,
  146. price: price
  147. });
  148. recipes.push(recipe);
  149. merchant.recipes.push(recipe);
  150. }
  151. }else{
  152. let recipe = new Recipe({
  153. posId: response[0].data.objects[i].item_data.variations[0].id,
  154. merchant: merchant._id,
  155. name: response[0].data.objects[i].item_data.name,
  156. price: response[0].data.objects[i].item_data.variations[0].item_variation_data.price_money.amount,
  157. ingredients: []
  158. });
  159. recipes.push(recipe);
  160. merchant.recipes.push(recipe);
  161. }
  162. }
  163. return Promise.all([Recipe.create(recipes), merchant.save()]);
  164. })
  165. .then((response)=>{
  166. req.session.user = response[1].session.sessionId;
  167. res.redirect("/dashboard");
  168. let body = {
  169. location_ids: [merchant.square.location],
  170. limit: 10000,
  171. query: {}
  172. };
  173. let options = {
  174. headers: {
  175. Authorization: `Bearer ${merchant.square.accessToken}`,
  176. "Content-Type": "application/json"
  177. }
  178. };
  179. return axios.post(`${process.env.SQUARE_ADDRESS}/v2/orders/search`, body, options);
  180. })
  181. .then(async (response)=>{
  182. let transactions = [];
  183. for(let i = 0; i < response.data.orders.length; i++){
  184. let transaction = new Transaction({
  185. merchant: merchant._id,
  186. date: new Date(response.data.orders[i].created_at),
  187. posId: response.data.orders[i].id,
  188. recipes: []
  189. });
  190. if(response.data.orders[i].line_items === undefined) continue;
  191. for(let j = 0; j < response.data.orders[i].line_items.length; j++){
  192. let item = response.data.orders[i].line_items[j];
  193. for(let k = 0; k < merchant.recipes.length; k++){
  194. if(merchant.recipes[k].posId === item.catalog_object_id){
  195. transaction.recipes.push({
  196. recipe: merchant.recipes[k]._id,
  197. quantity: parseInt(item.quantity)
  198. });
  199. }
  200. }
  201. }
  202. transactions.push(transaction);
  203. }
  204. let body = {
  205. location_ids: [merchant.square.location],
  206. limit: 10000,
  207. cursor: response.data.cursor,
  208. query: {}
  209. };
  210. let options = {
  211. headers: {
  212. Authorization: `Bearer ${merchant.square.accessToken}`,
  213. "Content-Type": "application/json"
  214. }
  215. };
  216. while(body.cursor !== undefined){
  217. let response = await axios.post(`${process.env.SQUARE_ADDRESS}/v2/orders/search`, body, options);
  218. body.cursor = response.data.cursor;
  219. for(let i = 0; i < response.data.orders.length; i++){
  220. let transaction = new Transaction({
  221. merchant: merchant._id,
  222. date: new Date(response.data.orders[i].created_at),
  223. posId: response.data.orders[i].id,
  224. recipes: []
  225. });
  226. if(responose.data.orders[i].line_items === undefined) continue;
  227. for(let j = 0; j < response.data.orders[i].line_items.length; j++){
  228. let item = response.data.orders[i].line_items[j];
  229. for(let k = 0; k < merchant.recipes.length; k++){
  230. if(merchant.recipes[k].posId === item.catalog_object_id){
  231. transaction.recipes.push({
  232. recipe: merchant.recipes[k]._id,
  233. quantity: parseInt(item.quantity)
  234. });
  235. }
  236. }
  237. }
  238. transactions.push(transaction);
  239. }
  240. }
  241. return Transaction.create(transactions);
  242. })
  243. .catch((err)=>{
  244. if(typeof(err) === "string"){
  245. req.session.error = err;
  246. }else if(err.name === "ValidationError"){
  247. req.session.error = err.errors[Object.keys(err.errors)[0]].properties.message;
  248. }else{
  249. req.session.error = "ERROR: UNABLE TO CREATE NEW USER";
  250. }
  251. return res.redirect("/");
  252. });
  253. },
  254. updateRecipes: function(req, res){
  255. let merchant = {};
  256. let merchantRecipes = [];
  257. let newRecipes = [];
  258. res.locals.merchant
  259. .populate("recipes")
  260. .execPopulate()
  261. .then((fetchedMerchant)=>{
  262. merchant = fetchedMerchant;
  263. return axios.post(`${process.env.SQUARE_ADDRESS}/v2/catalog/search`, {
  264. object_types: ["ITEM"]
  265. }, {
  266. headers: {
  267. Authorization: `Bearer ${merchant.posAccessToken}`
  268. }
  269. });
  270. })
  271. .then((response)=>{
  272. merchantRecipes = merchant.recipes.slice();
  273. for(let i = 0; i < response.data.objects.length; i++){
  274. let itemData = response.data.objects[i].item_data;
  275. for(let j = 0; j < itemData.variations.length; j++){
  276. let isFound = false;
  277. for(let k = 0; k < merchantRecipes.length; k++){
  278. if(itemData.variations[j].id === merchantRecipes[k].posId){
  279. merchantRecipes.splice(k, 1);
  280. k--;
  281. isFound = true;
  282. break;
  283. }
  284. }
  285. if(!isFound){
  286. let newRecipe = new Recipe({
  287. posId: itemData.variations[j].id,
  288. merchant: merchant._id,
  289. name: "",
  290. price: itemData.variations[j].item_variation_data.price_money.amount,
  291. ingredients: []
  292. });
  293. if(itemData.variations.length > 1){
  294. newRecipe.name = `${itemData.name} '${itemData.variations[j].item_variation_data.name}'`;
  295. }else{
  296. newRecipe.name = itemData.name;
  297. }
  298. newRecipes.push(newRecipe);
  299. merchant.recipes.push(newRecipe);
  300. }
  301. }
  302. }
  303. let ids = [];
  304. for(let i = 0; i < merchantRecipes.length; i++){
  305. ids.push(merchantRecipes[i]._id);
  306. for(let j = 0; j < merchant.recipes.length; j++){
  307. if(merchantRecipes[i]._id.toString() === merchant.recipes[j]._id.toString()){
  308. merchant.recipes.splice(j, 1);
  309. j--;
  310. break;
  311. }
  312. }
  313. }
  314. if(newRecipes.length > 0) Recipe.create(newRecipes);
  315. if(merchantRecipes.length > 0) Recipe.deleteMany({_id: {$in: ids}});
  316. return merchant.save();
  317. })
  318. .then((merchant)=>{
  319. return res.json({new: newRecipes, removed: merchantRecipes});
  320. })
  321. .catch((err)=>{
  322. if(typeof(err) === "string"){
  323. return res.json(err);
  324. }
  325. if(err.name === "ValidationError"){
  326. return res.json(err.errors[Object.keys(err.errors)[0]].properties.message);
  327. }
  328. return res.json("ERROR: UNABLE TO RETRIEVE RECIPE DATA FROM SQUARE");
  329. });
  330. }
  331. }