helper.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. const axios = require("axios");
  2. const Transaction = require("../models/transaction.js");
  3. module.exports = {
  4. getCloverData: async function(merchant){
  5. const subscriptionCheck = axios.get(`${process.env.CLOVER_ADDRESS}/v3/apps/${process.env.SUBLINE_CLOVER_APPID}/merchants/${merchant.posId}/billing_info?access_token=${merchant.posAccessToken}`);
  6. const transactionRetrieval = axios.get(`${process.env.CLOVER_ADDRESS}/v3/merchants/${merchant.posId}/orders?filter=modifiedTime>=${merchant.lastUpdatedTime}&expand=lineItems&expand=payment&access_token=${merchant.posAccessToken}`);
  7. await Promise.all([subscriptionCheck, transactionRetrieval])
  8. .then((response)=>{
  9. if(response[0].data.status !== "ACTIVE"){
  10. req.session.error = "SUBSCRIPTION EXPIRED. PLEASE RENEW ON CLOVER";
  11. return res.redirect("/");
  12. }
  13. const updatedTime = Date.now();
  14. //Create Subline transactions from Clover Transactions
  15. let transactions = [];
  16. for(let i = 0; i < response[1].data.elements.length; i++){
  17. let order = response[1].data.elements[i];
  18. if(order.paymentState !== "PAID"){
  19. break;
  20. }
  21. let newTransaction = new Transaction({
  22. merchant: merchant._id,
  23. date: new Date(order.createdTime),
  24. device: order.device.id,
  25. posId: order.id
  26. });
  27. //Go through lineItems from Clover
  28. //Get the appropriate recipe from Subline
  29. //Add it to the transaction or increment if existing
  30. for(let j = 0; j < order.lineItems.elements.length; j++){
  31. let recipe = {}
  32. for(let k = 0; k < merchant.recipes.length; k++){
  33. if(merchant.recipes[k].posId === order.lineItems.elements[j].item.id){
  34. recipe = merchant.recipes[k];
  35. break;
  36. }
  37. }
  38. if(recipe){
  39. let isNewRecipe = true;
  40. for(let k = 0; k < newTransaction.recipes.length; k++){
  41. if(newTransaction.recipes[k].recipe === recipe._id){
  42. newTransaction.recipes[k].quantity++;
  43. isNewRecipe = false;
  44. break;
  45. }
  46. }
  47. if(isNewRecipe){
  48. newTransaction.recipes.push({
  49. recipe: recipe._id,
  50. quantity: 1
  51. });
  52. }
  53. //Subtract ingredients from merchants total for each ingredient in a recipe
  54. for(let k = 0; k < recipe.ingredients.length; k++){
  55. let inventoryIngredient = {};
  56. for(let l = 0; l < merchant.inventory.length; l++){
  57. if(merchant.inventory[l].ingredient._id.toString() === recipe.ingredients[k].ingredient._id.toString()){
  58. inventoryIngredient = merchant.inventory[l];
  59. break;
  60. }
  61. }
  62. inventoryIngredient.quantity = inventoryIngredient.quantity - ingredient.quantity;
  63. }
  64. }
  65. }
  66. transactions.push(newTransaction);
  67. }
  68. merchant.lastUpdatedTime = updatedTime;
  69. //Remove any existing orders so that they can be replaced
  70. let ids = [];
  71. for(let i = 0; i < transactions.length; i++){
  72. ids.push(transactions[i].posId);
  73. }
  74. Transaction.deleteMany({posId: {$in: ids}});
  75. return Transaction.create(transactions);
  76. })
  77. .catch((err)=>{
  78. req.session.error = "ERROR: UNABLE TO RETRIEVE DATA FROM CLOVER";
  79. return res.redirect("/");
  80. });
  81. },
  82. getSquareData: function(merchant){
  83. let now = new Date().toISOString();
  84. now = `${now.substring(0, now.length - 1)}+00:00`;
  85. let before = new Date(merchant.lastUpdatedTime).toISOString();
  86. before = `${before.substring(0, before.length - 1)}+00:00`;
  87. let ingredients = {};
  88. return axios.post(`${process.env.SQUARE_ADDRESS}/v2/orders/search`, {
  89. location_ids: [merchant.squareLocation],
  90. query: {
  91. filter: {
  92. date_time_filter: {
  93. closed_at: {
  94. start_at: before,
  95. end_at: now
  96. }
  97. },
  98. state_filter: {
  99. states: ["COMPLETED"]
  100. }
  101. },
  102. sort: {
  103. sort_field: "CLOSED_AT",
  104. sort_order: "DESC"
  105. }
  106. }
  107. }, {
  108. headers: {
  109. Authorization: `Bearer ${merchant.posAccessToken}`
  110. }
  111. })
  112. .then((response)=>{
  113. let transactions = [];
  114. if(response.data.orders){
  115. for(let i = 0; i < response.data.orders.length; i++){
  116. let transaction = new Transaction({
  117. merchant: merchant,
  118. date: response.data.orders[i].created_at,
  119. posId: response.data.orders[i].id,
  120. recipes: []
  121. });
  122. for(let j = 0; j < response.data.orders[i].line_items.length; j++){
  123. for(let k = 0; k < merchant.recipes.length; k++){
  124. if(response.data.orders[i].line_items[j].catalog_object_id === merchant.recipes[k].posId){
  125. let quantitySold = parseInt(response.data.orders[i].line_items[j].quantity);
  126. transaction.recipes.push({
  127. recipe: merchant.recipes[k],
  128. quantity: quantitySold
  129. });
  130. for(let l = 0; l < merchant.recipes[k].ingredients.length; l++){
  131. let ingredient = merchant.recipes[k].ingredients[l];
  132. let quantity = quantitySold * ingredient.quantity
  133. ingredients[ingredient.ingredient] = ingredients[ingredient.ingredient] + quantity || quantity;
  134. }
  135. break;
  136. }
  137. }
  138. }
  139. transactions.push(transaction);
  140. }
  141. }
  142. return Transaction.create(transactions);
  143. })
  144. .then((transactions)=>{
  145. const keys = Object.keys(ingredients);
  146. for(let i = 0; i < keys.length; i++){
  147. for(let j = 0; j < merchant.inventory.length; j++){
  148. if(keys[i] === merchant.inventory[j].ingredient._id.toString()){
  149. merchant.inventory[j].quantity -= ingredients[keys[i]];
  150. break;
  151. }
  152. }
  153. }
  154. merchant.lastUpdatedTime = new Date();
  155. return transactions;
  156. })
  157. .catch((err)=>{
  158. return "ERROR: UNABLE TO UPDATE TRANSACTION DATA";
  159. });
  160. },
  161. convertQuantityToBaseUnit: function(quantity, unit){
  162. console.log(quantity);
  163. console.log(unit);
  164. switch(unit){
  165. case "kg": return quantity * 1000;
  166. case "oz": return quantity * 28.3495;
  167. case "lb": return quantity * 453.5924;
  168. case "ml": return quantity / 1000;
  169. case "tsp": return quantity / 202.8842;
  170. case "tbsp": return quantity / 67.6278;
  171. case "ozfl": return quantity / 33.8141;
  172. case "cup": return quantity / 4.1667;
  173. case "pt": return quantity / 2.1134;
  174. case "qt": return quantity / 1.0567;
  175. case "gal": return quantity * 3.7854;
  176. case "mm": return quantity / 1000;
  177. case "cm": return quantity / 100;
  178. case "in": return quantity / 39.3701;
  179. case "ft": return quantity / 3.2808;
  180. default: return quantity;
  181. }
  182. },
  183. convertPrice: function(price, unit){
  184. switch(unit){
  185. case "g":return price;
  186. case "kg": return price / 1000;
  187. case "oz": return price / 28.3495;
  188. case "lb": return price / 453.5924;
  189. case "ml": return price * 1000;
  190. case "l": return price;
  191. case "tsp": return price * 202.8842;
  192. case "tbsp": return price * 67.6278;
  193. case "ozfl": return price * 33.8141;
  194. case "cup": return price * 4.1667;
  195. case "pt": return price * 2.1134;
  196. case "qt": return price * 1.0567;
  197. case "gal": return price / 3.7854;
  198. case "mm": return price * 1000;
  199. case "cm": return price * 100;
  200. case "m": return price;
  201. case "in": return price * 39.3701;
  202. case "ft": return price * 3.2808;
  203. default: return price;
  204. }
  205. },
  206. generateId: function(length){
  207. let result = "";
  208. let characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  209. for(let i = 0; i < length; i++){
  210. result += characters.charAt(Math.floor(Math.random() * characters.length));
  211. }
  212. return result;
  213. },
  214. isSanitary: function(strings){
  215. let disallowed = ["\\", "<", ">", "$", "{", "}", "(", ")"];
  216. for(let i = 0; i < strings.length; i++){
  217. for(let j = 0; j < disallowed.length; j++){
  218. if(strings[i].includes(disallowed[j])){
  219. return false;
  220. }
  221. }
  222. }
  223. return true;
  224. },
  225. getUnitType: function(unit){
  226. let unitType = "";
  227. switch(unit){
  228. case "g": unitType = "mass"; break;
  229. case "kg": unitType = "mass"; break;
  230. case "oz": unitType = "mass"; break;
  231. case "lb": unitType = "mass"; break;
  232. case "ml": unitType = "volume"; break;
  233. case "l": unitType = "volume"; break;
  234. case "tsp": unitType = "volume"; break;
  235. case "tbsp": unitType = "volume"; break;
  236. case "ozfl": unitType = "volume"; break;
  237. case "cup": unitType = "volume"; break;
  238. case "pt": unitType = "volume"; break;
  239. case "qt": unitType = "volume"; break;
  240. case "gal": unitType = "volume"; break;
  241. case "mm": unitType = "length"; break;
  242. case "cm": unitType = "length"; break;
  243. case "m": unitType = "length"; break;
  244. case "in": unitType = "length"; break;
  245. case "ft": unitType = "length"; break;
  246. default: unitType = "other"; break;
  247. }
  248. return unitType;
  249. }
  250. }