squareData.js 15 KB

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