Merchant.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. class Ingredient{
  2. constructor(id, name, category, unitType, unit, parent){
  3. this.id = id;
  4. this.name = name;
  5. this.category = category;
  6. this.unitType = unitType;
  7. this.unit = unit;
  8. this.parent = parent;
  9. }
  10. convert(quantity){
  11. if(this.unitType === "mass"){
  12. switch(this.unit){
  13. case "g": break;
  14. case "kg": quantity /= 1000; break;
  15. case "oz": quantity /= 28.3495; break;
  16. case "lb": quantity /= 453.5924; break;
  17. }
  18. }else if(this.unitType === "volume"){
  19. switch(this.unit){
  20. case "ml": quantity *= 1000; break;
  21. case "l": break;
  22. case "tsp": quantity *= 202.8842; break;
  23. case "tbsp": quantity *= 67.6278; break;
  24. case "ozfl": quantity *= 33.8141; break;
  25. case "cup": quantity *= 4.1667; break;
  26. case "pt": quantity *= 2.1134; break;
  27. case "qt": quantity *= 1.0567; break;
  28. case "gal": quantity /= 3.7854; break;
  29. }
  30. }else if(this.unitType === "length"){
  31. switch(this.unit){
  32. case "mm": quantity *= 1000; break;
  33. case "cm": quantity *= 100; break;
  34. case "m": break;
  35. case "in": quantity *= 39.3701; break;
  36. case "ft": quantity *= 3.2808; break;
  37. }
  38. }
  39. return quantity;
  40. }
  41. }
  42. class Recipe{
  43. constructor(id, name, price, ingredients, parent){
  44. this.id = id;
  45. this.name = name;
  46. this.price = price;
  47. this.parent = parent;
  48. this.ingredients = [];
  49. for(let i = 0; i < ingredients.length; i++){
  50. for(let j = 0; j < parent.ingredients.length; j++){
  51. if(ingredients[i].ingredient === parent.ingredients[j].ingredient.id){
  52. this.ingredients.push({
  53. ingredient: parent.ingredients[j].ingredient,
  54. quantity: ingredients[i].quantity
  55. });
  56. break;
  57. }
  58. }
  59. }
  60. }
  61. }
  62. class Transaction{
  63. constructor(id, date, recipes, parent){
  64. this.id = id;
  65. this.parent = parent;
  66. this.date = new Date(date);
  67. this.recipes = [];
  68. for(let i = 0; i < recipes.length; i++){
  69. for(let j = 0; j < parent.recipes.length; j++){
  70. if(recipes[i].recipe === parent.recipes[j].id){
  71. this.recipes.push({
  72. recipe: parent.recipes[j],
  73. quantity: recipes[i].quantity
  74. });
  75. break;
  76. }
  77. }
  78. }
  79. }
  80. }
  81. class Order{
  82. constructor(id, name, date, ingredients, parent){
  83. this.id = id;
  84. this.name = name;
  85. this.date = new Date(date);
  86. this.ingredients = [];
  87. this.parent = parent;
  88. for(let i = 0; i < ingredients.length; i++){
  89. for(let j = 0; j < parent.ingredients.length; j++){
  90. if(ingredients[i].ingredient === parent.ingredients[j].ingredient.id){
  91. this.ingredients.push({
  92. ingredient: parent.ingredients[j].ingredient,
  93. quantity: ingredients[i].quantity,
  94. price: ingredients[i].price
  95. });
  96. }
  97. }
  98. }
  99. }
  100. convertPrice(unitType, unit, price){
  101. if(unitType === "mass"){
  102. switch(unit){
  103. case "g": break;
  104. case "kg": price *= 1000; break;
  105. case "oz": price *= 28.3495; break;
  106. case "lb": price *= 453.5924; break;
  107. }
  108. }else if(unitType === "volume"){
  109. switch(unit){
  110. case "ml": price /= 1000; break;
  111. case "l": break;
  112. case "tsp": price /= 202.8842; break;
  113. case "tbsp": price /= 67.6278; break;
  114. case "ozfl": price /= 33.8141; break;
  115. case "cup": price /= 4.1667; break;
  116. case "pt": price /= 2.1134; break;
  117. case "qt": price /= 1.0567; break;
  118. case "gal": price *= 3.7854; break;
  119. }
  120. }else if(unitType === "length"){
  121. switch(unit){
  122. case "mm": price /= 1000; break;
  123. case "cm": price /= 100; break;
  124. case "m": break;
  125. case "in": price /= 39.3701; break;
  126. case "ft": price /= 3.2808; break;
  127. }
  128. }
  129. return price;
  130. }
  131. }
  132. class Merchant{
  133. constructor(oldMerchant, transactions){
  134. this.name = oldMerchant.name;
  135. this.pos = oldMerchant.pos;
  136. this.ingredients = [];
  137. this.recipes = [];
  138. this.transactions = [];
  139. this.orders = [];
  140. this.units = {
  141. mass: ["g", "kg", "oz", "lb"],
  142. volume: ["ml", "l", "tsp", "tbsp", "ozfl", "cup", "pt", "qt", "gal"],
  143. length: ["mm", "cm", "m", "in", "foot"]
  144. }
  145. for(let i = 0; i < oldMerchant.inventory.length; i++){
  146. this.ingredients.push({
  147. ingredient: new Ingredient(
  148. oldMerchant.inventory[i].ingredient._id,
  149. oldMerchant.inventory[i].ingredient.name,
  150. oldMerchant.inventory[i].ingredient.category,
  151. oldMerchant.inventory[i].ingredient.unitType,
  152. oldMerchant.inventory[i].defaultUnit,
  153. this
  154. ),
  155. quantity: oldMerchant.inventory[i].quantity
  156. });
  157. }
  158. for(let i = 0; i < oldMerchant.recipes.length; i++){
  159. this.recipes.push(new Recipe(
  160. oldMerchant.recipes[i]._id,
  161. oldMerchant.recipes[i].name,
  162. oldMerchant.recipes[i].price,
  163. oldMerchant.recipes[i].ingredients,
  164. this
  165. ));
  166. }
  167. for(let i = 0; i < transactions.length; i++){
  168. this.transactions.push(new Transaction(
  169. transactions[i]._id,
  170. transactions[i].date,
  171. transactions[i].recipes,
  172. this
  173. ));
  174. }
  175. }
  176. /*
  177. Updates all specified item in the merchant's inventory and updates the page
  178. If ingredient doesn't exist, add it
  179. ingredients = {
  180. ingredient: Ingredient object,
  181. quantity: new quantity
  182. }
  183. remove = set true if removing
  184. isOrder = set true if this is coming from an order
  185. */
  186. editIngredients(ingredients, remove = false, isOrder = false){
  187. for(let i = 0; i < ingredients.length; i++){
  188. let isNew = true;
  189. for(let j = 0; j < merchant.ingredients.length; j++){
  190. if(merchant.ingredients[j].ingredient === ingredients[i].ingredient){
  191. if(remove){
  192. merchant.ingredients.splice(j, 1);
  193. }else if(!remove && isOrder){
  194. merchant.ingredients[j].quantity += ingredients[i].quantity;
  195. }else{
  196. merchant.ingredients[j].quantity = ingredients[i].quantity;
  197. }
  198. isNew = false;
  199. break;
  200. }
  201. }
  202. if(isNew){
  203. merchant.ingredients.push({
  204. ingredient: ingredients[i].ingredient,
  205. quantity: parseFloat(ingredients[i].quantity)
  206. });
  207. }
  208. }
  209. homeStrandObj.drawInventoryCheckCard();
  210. ingredientsStrandObj.populateByProperty("category");
  211. addIngredientsComp.isPopulated = false;
  212. closeSidebar();
  213. }
  214. /*
  215. Updates a recipe in the merchants list of recipes
  216. Can create, edit or remove
  217. recipe = [Recipe object]
  218. remove = will remove recipe when true
  219. */
  220. editRecipes(recipes, remove = false){
  221. let isNew = true;
  222. for(let i = 0; i < recipes.length; i++){
  223. for(let j = 0; j < this.recipes.length; j++){
  224. if(recipes[i] === this.recipes[j]){
  225. if(remove){
  226. this.recipes.splice(j, 1);
  227. }else{
  228. this.recipes[j] = recipes[i];
  229. }
  230. isNew = false;
  231. break;
  232. }
  233. }
  234. if(isNew){
  235. merchant.recipes.push(recipes[i]);
  236. }
  237. }
  238. recipeBookStrandObj.populateRecipes();
  239. closeSidebar();
  240. }
  241. /*
  242. Updates a list of orders in the merchants list of orders
  243. Create/edit/remove
  244. orders = [Order object]
  245. remove = will remove order when true
  246. */
  247. editOrders(orders, remove = false){
  248. for(let i = 0; i < orders.length; i++){
  249. let isNew = true;
  250. for(let j = 0; j < this.orders.length; j++){
  251. if(orders[i] === this.orders[j]){
  252. if(remove){
  253. this.orders.splice(j, 1);
  254. }else{
  255. this.orders[j] = orders[i];
  256. }
  257. isNew = false;
  258. break;
  259. }
  260. }
  261. if(isNew){
  262. this.orders.push(orders[i]);
  263. }
  264. }
  265. ordersStrandObj.populate();
  266. closeSidebar();
  267. }
  268. editTransactions(transaction, remove = false){
  269. let isNew = true;
  270. for(let i = 0; i < this.transactions.length; i++){
  271. if(this.transactions[i] === transaction){
  272. if(remove){
  273. this.transactions.splice(i, 1);
  274. }
  275. isNew = false;
  276. break;
  277. }
  278. }
  279. if(isNew){
  280. this.transactions.push(transaction);
  281. this.transactions.sort((a, b) => a.date > b.date ? 1 : -1);
  282. }
  283. transactionsStrandObj.isPopulated = false;
  284. transactionsStrandObj.display();
  285. closeSidebar();
  286. }
  287. /*
  288. Gets the indices of two dates from transactions
  289. Inputs
  290. from: starting date
  291. to: ending date (default to now)
  292. Output
  293. Array containing starting index and ending index
  294. Note: Will return false if it cannot find both necessary dates
  295. */
  296. transactionIndices(from, to = new Date()){
  297. let indices = [];
  298. for(let i = 0; i < this.transactions.length; i++){
  299. if(this.transactions[i].date > from){
  300. indices.push(i);
  301. break;
  302. }
  303. }
  304. for(let i = this.transactions.length - 1; i >=0; i--){
  305. if(this.transactions[i].date < to){
  306. indices.push(i);
  307. break;
  308. }
  309. }
  310. if(indices.length < 2){
  311. return false;
  312. }
  313. return indices;
  314. }
  315. revenue(indices){
  316. let total = 0;
  317. for(let i = indices[0]; i <= indices[1]; i++){
  318. for(let j = 0; j < this.transactions[i].recipes.length; j++){
  319. for(let k = 0; k < this.recipes.length; k++){
  320. if(this.transactions[i].recipes[j].recipe === this.recipes[k]){
  321. total += this.transactions[i].recipes[j].quantity * this.recipes[k].price;
  322. }
  323. }
  324. }
  325. }
  326. return total / 100;
  327. }
  328. /*
  329. Gets the quantity of each ingredient sold between two dates (dateRange)
  330. Inputs
  331. dateRange: list containing a start date and an end date
  332. Return:
  333. [{
  334. ingredient: Ingredient object,
  335. quantity: quantity of ingredient sold
  336. }]
  337. */
  338. ingredientsSold(dateRange){
  339. if(!dateRange){
  340. return false;
  341. }
  342. let recipes = this.recipesSold(dateRange);
  343. let ingredientList = [];
  344. for(let i = 0; i < recipes.length; i++){
  345. for(let j = 0; j < recipes[i].recipe.ingredients.length; j++){
  346. let exists = false;
  347. for(let k = 0; k < ingredientList.length; k++){
  348. if(ingredientList[k].ingredient === recipes[i].recipe.ingredients[j].ingredient){
  349. exists = true;
  350. ingredientList[k].quantity += recipes[i].quantity * recipes[i].recipe.ingredients[j].quantity;
  351. break;
  352. }
  353. }
  354. if(!exists){
  355. ingredientList.push({
  356. ingredient: recipes[i].recipe.ingredients[j].ingredient,
  357. quantity: recipes[i].quantity * recipes[i].recipe.ingredients[j].quantity
  358. });
  359. }
  360. }
  361. }
  362. return ingredientList;
  363. }
  364. singleIngredientSold(dateRange, ingredient){
  365. let total = 0;
  366. for(let i = dateRange[0]; i < dateRange[1]; i++){
  367. for(let j = 0; j < this.transactions[i].recipes.length; j++){
  368. for(let k = 0; k < this.transactions[i].recipes[j].recipe.ingredients.length; k++){
  369. if(this.transactions[i].recipes[j].recipe.ingredients[k].ingredient === ingredient.ingredient){
  370. total += this.transactions[i].recipes[j].recipe.ingredients[k].quantity;
  371. break;
  372. }
  373. }
  374. }
  375. }
  376. return total;
  377. }
  378. /*
  379. Gets the number of recipes sold between two dates (dateRange)
  380. Inputs:
  381. dateRange: array containing a start date and an end date
  382. Return:
  383. [{
  384. recipe: a recipe object
  385. quantity: quantity of the recipe sold
  386. }]
  387. */
  388. recipesSold(dateRange){
  389. let recipeList = [];
  390. for(let i = dateRange[0]; i <= dateRange[1]; i++){
  391. for(let j = 0; j < this.transactions[i].recipes.length; j++){
  392. let exists = false;
  393. for(let k = 0; k < recipeList.length; k++){
  394. if(recipeList[k].recipe === this.transactions[i].recipes[j].recipe){
  395. exists = true;
  396. recipeList[k].quantity += this.transactions[i].recipes[j].quantity;
  397. break;
  398. }
  399. }
  400. if(!exists){
  401. recipeList.push({
  402. recipe: this.transactions[i].recipes[j].recipe,
  403. quantity: this.transactions[i].recipes[j].quantity
  404. });
  405. }
  406. }
  407. }
  408. return recipeList;
  409. }
  410. /*
  411. Create revenue data for graphing
  412. Input:
  413. dateRange: [start index, end index] (this.transactionIndices)
  414. Return:
  415. [total revenue for each day]
  416. */
  417. graphDailyRevenue(dateRange){
  418. if(!dateRange){
  419. return false;
  420. }
  421. let dataList = new Array(30).fill(0);
  422. let currentDate = this.transactions[dateRange[0]].date;
  423. let arrayIndex = 0;
  424. for(let i = dateRange[0]; i <= dateRange[1]; i++){
  425. if(this.transactions[i].date.getDate() !== currentDate.getDate()){
  426. currentDate = this.transactions[i].date;
  427. arrayIndex++;
  428. }
  429. for(let j = 0; j < this.transactions[i].recipes.length; j++){
  430. dataList[arrayIndex] += (this.transactions[i].recipes[j].recipe.price / 100) * this.transactions[i].recipes[j].quantity;
  431. }
  432. }
  433. return dataList;
  434. }
  435. /*
  436. Groups all of the merchant's ingredients by their category
  437. Return: [{
  438. name: category name,
  439. ingredients: [Ingredient Object]
  440. }]
  441. */
  442. categorizeIngredients(){
  443. let ingredientsByCategory = [];
  444. for(let i = 0; i < this.ingredients.length; i++){
  445. let categoryExists = false;
  446. for(let j = 0; j < ingredientsByCategory.length; j++){
  447. if(this.ingredients[i].ingredient.category === ingredientsByCategory[j].name){
  448. ingredientsByCategory[j].ingredients.push(this.ingredients[i]);
  449. categoryExists = true;
  450. break;
  451. }
  452. }
  453. if(!categoryExists){
  454. ingredientsByCategory.push({
  455. name: this.ingredients[i].ingredient.category,
  456. ingredients: [this.ingredients[i]]
  457. });
  458. }
  459. }
  460. return ingredientsByCategory;
  461. }
  462. unitizeIngredients(){
  463. let ingredientsByUnit = [];
  464. for(let i = 0; i < this.ingredients.length; i++){
  465. let unitExists = false;
  466. for(let j = 0; j < ingredientsByUnit.length; j++){
  467. if(this.ingredients[i].ingredient.unit === ingredientsByUnit[j].name){
  468. ingredientsByUnit[j].ingredients.push(this.ingredients[i]);
  469. unitExists = true;
  470. break;
  471. }
  472. }
  473. if(!unitExists){
  474. ingredientsByUnit.push({
  475. name: this.ingredients[i].ingredient.unit,
  476. ingredients: [this.ingredients[i]]
  477. });
  478. }
  479. }
  480. return ingredientsByUnit;
  481. }
  482. getRecipesForIngredient(ingredient){
  483. let recipes = [];
  484. for(let i = 0; i < this.recipes.length; i++){
  485. for(let j = 0; j < this.recipes[i].ingredients.length; j++){
  486. if(this.recipes[i].ingredients[j].ingredient === ingredient){
  487. recipes.push(this.recipes[i]);
  488. }
  489. }
  490. }
  491. return recipes;
  492. }
  493. }