squareData.js 14 KB

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