Account.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import EncryptionHandler from "../EncryptionHandler.js";
  2. import Notifier from "../Notifier.js";
  3. import Format from "../Format.js";
  4. import Transaction from "./Transaction.js";
  5. import Income from "./Income.js";
  6. import Bill from "./Bill.js";
  7. import Allowance from "./Allowance.js";
  8. export default class Account{
  9. constructor(id, iv, data){
  10. this._id = id;
  11. this._iv = iv;
  12. this._name = data.name;
  13. this._balance = data.balance;
  14. this._income = data.income.map(Income.fromObject);
  15. this._bills = data.bills.map(Bill.fromObject);
  16. this._allowances = data.allowances.map(Allowance.fromObject);
  17. this._transactions = [];
  18. this._populated = false;
  19. }
  20. get id(){
  21. return this._id;
  22. }
  23. get name(){
  24. return this._name;
  25. }
  26. get balance(){
  27. return this._balance;
  28. }
  29. get transactions(){
  30. return this._transactions;
  31. }
  32. get income(){
  33. return this._income;
  34. }
  35. get bills(){
  36. return this._bills;
  37. }
  38. get allowances(){
  39. return this._allowances;
  40. }
  41. get isPopulated(){
  42. return this._populated;
  43. }
  44. getDiscretionary(){
  45. const income = this.incomeTotal();
  46. return income - this.billsTotal() - this.allowancesTotal(income);
  47. }
  48. set isPopulated(v){
  49. if(typeof v === "boolean") this._populated = v;
  50. }
  51. getDiscretionarySpent(){
  52. let total = 0;
  53. for(let i = 0; i < this._transactions.length; i++){
  54. if(this._transactions[i].category.name === "Discretionary") total += this._transactions[i].amount;
  55. }
  56. return total;
  57. }
  58. getIncomeSum(){
  59. let total = 0;
  60. for(let i = 0; i < this._income.length; i++){
  61. if(this._income[i].active) total += this._income[i].amount;
  62. }
  63. return total;
  64. }
  65. getCategory(category, categoryId){
  66. if(category === "discretionary") return {name: "Discretionary"};
  67. let list;
  68. switch(category){
  69. case "income": list = this._income; break;
  70. case "bill": list = this._bills; break;
  71. case "allowance": list = this._allowances; break;
  72. }
  73. for(let i = 0; i < list.length; i++){
  74. if(list[i].id === categoryId) return list[i];
  75. }
  76. }
  77. categorySpent(category){
  78. let total = 0;
  79. for(let i = 0; i < this._transactions.length; i++){
  80. if(this._transactions[i].categoryId === category.id){
  81. total += this._transactions[i].rawAmount;
  82. }
  83. }
  84. return total;
  85. }
  86. incomeTotal(){
  87. let income = 0;
  88. for(let i = 0; i < this._income.length; i++){
  89. if(this._income[i].active){
  90. income += this._income[i].amount;
  91. }
  92. }
  93. return income;
  94. }
  95. billsTotal(){
  96. let bills = 0;
  97. for(let i = 0; i < this._bills.length; i++){
  98. bills += this._bills[i].amount;
  99. }
  100. return bills;
  101. }
  102. allowancesTotal(income){
  103. let allowances = 0;
  104. for(let i = 0; i < this._allowances.length; i++){
  105. if(!this._allowances[i].active) continue;
  106. if(this._allowances[i].isPercent){
  107. allowances += (this._allowances[i].amount / 100) * income;
  108. }else{
  109. allowances += this._allowances[i].amount;
  110. }
  111. }
  112. return allowances;
  113. }
  114. static async create(name, balance){
  115. const data = {
  116. name: name,
  117. balance: Format.dollarsToCents(balance),
  118. income: [],
  119. bills: [],
  120. allowances: []
  121. };
  122. const iv = encryptionHandler.generateIv();
  123. const encryptedData = await encryptionHandler.encrypt(data, iv);
  124. let response;
  125. try{
  126. response = await fetch("/api/account", {
  127. method: "POST",
  128. headers: {"Content-Type": "application/json"},
  129. body: JSON.stringify({data: encryptedData, iv})
  130. });
  131. response = await response.json();
  132. }catch(e){
  133. new Notifier("error", "Something went wrong, try refreshing the page");
  134. }
  135. if(response.error) new Notifier("error", response.error.message);
  136. return new Account(response.id, iv, data);
  137. }
  138. async addIncome(name, amount){
  139. this._income.push(Income.create(name, amount));
  140. await this.save();
  141. }
  142. async addBill(name, amount){
  143. this._bills.push(Bill.create(name, Format.dollarsToCents(amount)));
  144. await this.save();
  145. }
  146. async addAllowance(name, amount, isPercent){
  147. if(isPercent){
  148. amount = Number(amount);
  149. }else{
  150. amount = Format.dollarsToCents(amount);
  151. }
  152. this._allowances.push(Allowance.create(name, amount, isPercent));
  153. await this.save();
  154. }
  155. addTransaction(transaction){
  156. this._transactions.push(transaction);
  157. console.log(transaction.category.type);
  158. if(transaction.category.type === "Income"){
  159. this._balance += transaction.amount;
  160. }else{
  161. this._balance -= transaction.amount;
  162. }
  163. this.save();
  164. this.sortTransactions();
  165. }
  166. addManyTransactions(transactions){
  167. this._transactions.push(...transactions);
  168. this.sortTransactions();
  169. }
  170. removeTransaction(transaction){
  171. if(transaction instanceof Transaction){
  172. for(let i = 0; i < this._transactions.length; i++){
  173. if(this._transactions[i] === transaction){
  174. this._transactions.splice(i, 1);
  175. break;
  176. }
  177. }
  178. }
  179. }
  180. sortTransactions(){
  181. this._transactions.sort((a, b)=>{a > b ? 1 : -1});
  182. }
  183. listIncome(){
  184. const income = [];
  185. for(let i = 0; i < this._income.length; i++){
  186. income.push({
  187. id: this._income[i].id,
  188. name: this._income[i].name
  189. });
  190. }
  191. return income;
  192. }
  193. listBills(){
  194. const bills = [];
  195. for(let i = 0; i < this._bills.length; i++){
  196. bills.push({
  197. id: this._bills[i].id,
  198. name: this._bills[i].name
  199. });
  200. }
  201. return bills;
  202. }
  203. listAllowances(){
  204. const allowances = [];
  205. for(let i = 0; i < this._allowances.length; i++){
  206. allowances.push({
  207. id: this._allowances[i].id,
  208. name: this._allowances[i].name
  209. });
  210. }
  211. return allowances;
  212. }
  213. async populateTransactions(){
  214. if(this._populated) return;
  215. let from = new Date();
  216. from.setDate(1);
  217. const to = new Date();
  218. let response;
  219. try{
  220. response = await fetch("/api/transaction/search", {
  221. method: "POST",
  222. headers: {"Content-Type": "application/json"},
  223. body: JSON.stringify({
  224. account: this._id,
  225. from: Format.transactionDate(from),
  226. to: Format.transactionDate(to)
  227. })
  228. });
  229. }catch(e){
  230. new Notifier("error", "Something went wrong, try refreshing the page");
  231. }
  232. if(response.error){
  233. new Notifier("error", response.error.message);
  234. }else{
  235. for(let i = 0; i < response.length; i++){
  236. this._transactions = Transaction.decrypt(response[i]);
  237. }
  238. this._populated = true;
  239. }
  240. }
  241. async save(){
  242. const data = {
  243. name: this._name,
  244. balance: this._balance,
  245. income: this._income.map(i => i.serialize()),
  246. bills: this._bills.map(b => b.serialize()),
  247. allowances: this._allowances.map(a => a.serialize())
  248. };
  249. const encryptedData = await encryptionHandler.encrypt(data, this._iv);
  250. let response;
  251. try{
  252. response = await fetch("/api/account", {
  253. method: "PUT",
  254. headers: {"Content-Type": "application/json"},
  255. body: JSON.stringify({
  256. id: this._id,
  257. data: encryptedData
  258. })
  259. });
  260. }catch(e){
  261. new Notifier("error", "Something went wrong, try refreshing the page");
  262. }
  263. if(response.error) new Notifier("error", response.error.message);
  264. }
  265. }