squareData.js 14 KB

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