diff --git a/src/store/account.js b/src/store/account.js index 71d27b5d..9e94ae24 100644 --- a/src/store/account.js +++ b/src/store/account.js @@ -1,3 +1,4 @@ +import accountConstants from '../consts/account.js'; import services from '../lib/services.js'; import logger from '../lib/logger.js'; @@ -271,6 +272,48 @@ function deleteAccount(context, { account, beforeResolve }) { }); } +function allPlainAccounts(state) { + const allAccounts = []; + + for (let i = 0; i < state.allAccounts.length; i++) { + const account = state.allAccounts[i]; + + if (account.type === accountConstants.allAccountTypes.SingleAccount) { + allAccounts.push(account); + } else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) { + for (let j = 0; j < account.subAccounts.length; j++) { + const subAccount = account.subAccounts[j]; + allAccounts.push(subAccount); + } + } + } + + return allAccounts; +} + +function allVisiblePlainAccounts(state) { + const allVisibleAccounts = []; + + for (let i = 0; i < state.allAccounts.length; i++) { + const account = state.allAccounts[i]; + + if (account.hidden) { + continue; + } + + if (account.type === accountConstants.allAccountTypes.SingleAccount) { + allVisibleAccounts.push(account); + } else if (account.type === accountConstants.allAccountTypes.MultiSubAccounts) { + for (let j = 0; j < account.subAccounts.length; j++) { + const subAccount = account.subAccounts[j]; + allVisibleAccounts.push(subAccount); + } + } + } + + return allVisibleAccounts; +} + function allAvailableAccountsCount(state) { let allAccountCount = 0; @@ -313,6 +356,8 @@ export default { updateAccountDisplayOrders, hideAccount, deleteAccount, + allPlainAccounts, + allVisiblePlainAccounts, allAvailableAccountsCount, allVisibleAccountsCount, } diff --git a/src/store/index.js b/src/store/index.js index aa6588e4..5955189a 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -17,6 +17,9 @@ import { REMOVE_ACCOUNT_FROM_ACCOUNT_LIST, UPDATE_ACCOUNT_LIST_INVALID_STATE, + LOAD_TRANSACTION_LIST, + UPDATE_TRANSACTION_LIST_INVALID_STATE, + LOAD_TRANSACTION_CATEGORY_LIST, ADD_CATEGORY_TO_TRANSACTION_CATEGORY_LIST, SAVE_CATEGORY_IN_TRANSACTION_CATEGORY_LIST, @@ -39,6 +42,7 @@ import twoFactorAuth from './twoFactorAuth.js'; import token from './token.js'; import exchangeRates from './exchangeRates.js'; import account from './account.js'; +import transaction from './transaction.js'; import transactionCategory from './transactionCategory.js'; import transactionTag from './transactionTag.js'; @@ -51,35 +55,44 @@ const stores = { allAccountsMap: {}, allCategorizedAccounts: {}, accountListStateInvalid: true, + transactions: [], + transactionListStateInvalid: true, allTransactionCategories: {}, allTransactionCategoriesMap: {}, transactionCategoryListStateInvalid: true, allTransactionTags: [], allTransactionTagsMap: {}, transactionTagListStateInvalid: true, - transactions: [], }, getters: { currentUserNickname: user.currentUserNickname, currentUserDefaultCurrency: user.currentUserDefaultCurrency, exchangeRatesLastUpdateDate: exchangeRates.exchangeRatesLastUpdateDate, getExchangedAmount: exchangeRates.getExchangedAmount, + allPlainAccounts: account.allPlainAccounts, + allVisiblePlainAccounts: account.allVisiblePlainAccounts, allAvailableAccountsCount: account.allAvailableAccountsCount, allVisibleAccountsCount: account.allVisibleAccountsCount, }, mutations: { [RESET_STATE] (state) { state.latestExchangeRates = {}; + state.allAccounts = []; state.allAccountsMap = {}; state.allCategorizedAccounts = {}; state.accountListStateInvalid = true; + + state.transactions = []; + state.transactionListStateInvalid = true; + state.allTransactionCategories = {}; state.allTransactionCategoriesMap = {}; + state.transactionCategoryListStateInvalid = true; + state.allTransactionTags = []; state.allTransactionTagsMap = {}; state.transactionTagListStateInvalid = true; - state.transactions = []; exchangeRates.clearExchangeRatesFromLocalStorage(); }, @@ -208,6 +221,12 @@ const stores = { [UPDATE_ACCOUNT_LIST_INVALID_STATE] (state, invalidState) { state.accountListStateInvalid = invalidState; }, + [LOAD_TRANSACTION_LIST] (state, transactions) { + state.transactions = transactions; + }, + [UPDATE_TRANSACTION_LIST_INVALID_STATE] (state, invalidState) { + state.transactionListStateInvalid = invalidState; + }, [LOAD_TRANSACTION_CATEGORY_LIST] (state, allCategories) { state.allTransactionCategories = allCategories; state.allTransactionCategoriesMap = {}; @@ -386,6 +405,9 @@ const stores = { hideAccount: account.hideAccount, deleteAccount: account.deleteAccount, + getTransaction: transaction.getTransaction, + saveTransaction: transaction.saveTransaction, + loadAllCategories: transactionCategory.loadAllCategories, getCategory: transactionCategory.getCategory, saveCategory: transactionCategory.saveCategory, diff --git a/src/store/mutations.js b/src/store/mutations.js index f3932fc0..f0dd0733 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -13,6 +13,9 @@ export const UPDATE_ACCOUNT_VISIBILITY_IN_ACCOUNT_LIST = 'UPDATE_ACCOUNT_VISIBIL export const REMOVE_ACCOUNT_FROM_ACCOUNT_LIST = 'REMOVE_ACCOUNT_FROM_ACCOUNT_LIST'; export const UPDATE_ACCOUNT_LIST_INVALID_STATE = 'UPDATE_ACCOUNT_LIST_INVALID_STATE'; +export const LOAD_TRANSACTION_LIST = 'LOAD_TRANSACTION_LIST'; +export const UPDATE_TRANSACTION_LIST_INVALID_STATE = 'UPDATE_TRANSACTION_LIST_INVALID_STATE'; + export const LOAD_TRANSACTION_CATEGORY_LIST = 'LOAD_TRANSACTION_CATEGORY_LIST'; export const ADD_CATEGORY_TO_TRANSACTION_CATEGORY_LIST = 'ADD_CATEGORY_TO_TRANSACTION_CATEGORY_LIST'; export const SAVE_CATEGORY_IN_TRANSACTION_CATEGORY_LIST = 'SAVE_CATEGORY_IN_TRANSACTION_CATEGORY_LIST'; diff --git a/src/store/transaction.js b/src/store/transaction.js new file mode 100644 index 00000000..35d202a5 --- /dev/null +++ b/src/store/transaction.js @@ -0,0 +1,86 @@ +import services from '../lib/services.js'; +import logger from '../lib/logger.js'; + +import { + LOAD_TRANSACTION_LIST, UPDATE_ACCOUNT_LIST_INVALID_STATE, + UPDATE_TRANSACTION_LIST_INVALID_STATE +} from './mutations.js'; + +function getTransaction(context, { transactionId }) { + return new Promise((resolve, reject) => { + services.getTransaction({ + id: transactionId + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to get transaction' }); + return; + } + + context.commit(LOAD_TRANSACTION_LIST, data.result); + context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, false); + + resolve(data.result); + }).catch(error => { + logger.error('failed to load transaction info', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + reject({ message: 'Unable to get transaction' }); + } else { + reject(error); + } + }); + }); +} + +function saveTransaction(context, { transaction }) { + return new Promise((resolve, reject) => { + let promise = null; + + if (!transaction.id) { + promise = services.addTransaction(transaction); + } else { + promise = services.modifyTransaction(transaction); + } + + promise.then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + if (!transaction.id) { + reject({ message: 'Unable to add transaction' }); + } else { + reject({ message: 'Unable to save transaction' }); + } + return; + } + + context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, true); + context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true); + + resolve(data.result); + }).catch(error => { + logger.error('failed to save transaction', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + if (!transaction.id) { + reject({ message: 'Unable to add transaction' }); + } else { + reject({ message: 'Unable to save transaction' }); + } + } else { + reject(error); + } + }); + }); +} + +export default { + getTransaction, + saveTransaction +} diff --git a/src/views/mobile/transactions/Edit.vue b/src/views/mobile/transactions/Edit.vue index 0badf087..0ecde4c9 100644 --- a/src/views/mobile/transactions/Edit.vue +++ b/src/views/mobile/transactions/Edit.vue @@ -148,7 +148,7 @@ @@ -170,7 +170,7 @@ @@ -266,10 +266,6 @@ export default { tagIds: [], comment: '' }, - allAccounts: [], - categoriedAccounts: [], - allCategories: {}, - allTags: [], loading: true, submitting: false, showAccountBalance: self.$settings.isShowAccountBalance(), @@ -317,6 +313,82 @@ export default { return ''; } }, + allAccounts() { + return this.$store.getters.allPlainAccounts; + }, + allVisibleAccounts() { + return this.$store.getters.allVisiblePlainAccounts; + }, + categorizedAccounts() { + const categorizedAccounts = this.$utilities.copyObjectTo(this.$utilities.getCategorizedAccounts(this.allVisibleAccounts), {}); + + for (let category in categorizedAccounts) { + if (!Object.prototype.hasOwnProperty.call(categorizedAccounts, category)) { + continue; + } + + const accountCategory = categorizedAccounts[category]; + + if (accountCategory.accounts) { + for (let i = 0; i < accountCategory.accounts.length; i++) { + const account = accountCategory.accounts[i]; + + if (this.showAccountBalance && account.isAsset) { + account.displayBalance = this.$options.filters.currency(account.balance, account.currency); + } else if (this.showAccountBalance && account.isLiability) { + account.displayBalance = this.$options.filters.currency(-account.balance, account.currency); + } else { + account.displayBalance = '***'; + } + } + } + + if (this.showAccountBalance) { + const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category); + let totalBalance = 0; + let hasUnCalculatedAmount = false; + + for (let i = 0; i < accountsBalance.length; i++) { + if (accountsBalance[i].currency === this.defaultCurrency) { + if (accountsBalance[i].isAsset) { + totalBalance += accountsBalance[i].balance; + } else if (accountsBalance[i].isLiability) { + totalBalance -= accountsBalance[i].balance; + } + } else { + const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); + + if (!this.$utilities.isNumber(balance)) { + hasUnCalculatedAmount = true; + continue; + } + + if (accountsBalance[i].isAsset) { + totalBalance += Math.floor(balance); + } else if (accountsBalance[i].isLiability) { + totalBalance -= Math.floor(balance); + } + } + } + + if (hasUnCalculatedAmount) { + totalBalance = totalBalance + '+'; + } + + accountCategory.displayBalance = this.$options.filters.currency(totalBalance, this.defaultCurrency); + } else { + accountCategory.displayBalance = '***'; + } + } + + return categorizedAccounts; + }, + allCategories() { + return this.$store.state.allTransactionCategories; + }, + allTags() { + return this.$store.state.allTransactionTags; + }, defaultCurrency() { return this.$store.getters.currentUserDefaultCurrency || this.$t('default.currency'); }, @@ -364,13 +436,13 @@ export default { } else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Transfer) { let sourceAccount, destinationAccount = null; - for (let i = 0; i < this.allAccounts.length; i++) { - if (this.allAccounts[i].id === this.transaction.sourceAccountId) { - sourceAccount = this.allAccounts[i]; + for (let i = 0; i < this.allVisibleAccounts.length; i++) { + if (this.allVisibleAccounts[i].id === this.transaction.sourceAccountId) { + sourceAccount = this.allVisibleAccounts[i]; } - if (this.allAccounts[i].id === this.transaction.destinationAccountId) { - destinationAccount = this.allAccounts[i]; + if (this.allVisibleAccounts[i].id === this.transaction.destinationAccountId) { + destinationAccount = this.allVisibleAccounts[i]; } if (sourceAccount && destinationAccount) { @@ -426,9 +498,9 @@ export default { self.loading = true; const promises = [ - self.$services.getAllAccounts({ visibleOnly: true }), - self.$services.getAllTransactionCategories({}), - self.$services.getAllTransactionTags() + self.$store.dispatch('loadAllAccounts', { force: false }), + self.$store.dispatch('loadAllCategories', { force: false }), + self.$store.dispatch('loadAllTags', { force: false }) ]; if (query.id) { @@ -436,7 +508,7 @@ export default { self.editTransactionId = query.id; } - promises.push(self.$services.getTransaction({ id: query.id })); + promises.push(self.$store.dispatch('getTransaction', { transactionId: query.id })); } if (query.type && query.type !== '0' && @@ -446,43 +518,12 @@ export default { } Promise.all(promises).then(function (responses) { - const accountData = responses[0].data; - const categoryData = responses[1].data; - const tagData = responses[2].data; - - if (!accountData || !accountData.success || !accountData.result) { - self.$toast('Unable to get account list'); - router.back(); - return; - } - - if (!categoryData || !categoryData.success || !categoryData.result) { - self.$toast('Unable to get category list'); - router.back(); - return; - } - - if (!tagData || !tagData.success || !tagData.result) { - self.$toast('Unable to get tag list'); - router.back(); - return; - } - - if (query.id && (!responses[3] || !responses[3].data || !responses[3].data.success || !responses[3].data.result)) { + if (query.id && !responses[3]) { self.$toast('Unable to get transaction'); router.back(); return; } - self.allAccounts = self.$utilities.getPlainAccounts(accountData.result); - self.setAccountsDisplayBalance(self.allAccounts); - - self.categoriedAccounts = self.$utilities.getCategorizedAccounts(self.allAccounts); - self.setCategoriesDisplayBalance(self.categoriedAccounts); - - self.allCategories = categoryData.result; - self.allTags = tagData.result; - if (self.allCategories[self.$constants.category.allCategoryTypes.Expense] && self.allCategories[self.$constants.category.allCategoryTypes.Expense].length) { if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Expense], query.categoryId)) { @@ -516,10 +557,10 @@ export default { } } - if (self.allAccounts.length) { + if (self.allVisibleAccounts.length) { if (query.accountId && query.accountId !== '0') { - for (let i = 0; i < self.allAccounts.length; i++) { - if (self.allAccounts[i].id === query.accountId) { + for (let i = 0; i < self.allVisibleAccounts.length; i++) { + if (self.allVisibleAccounts[i].id === query.accountId) { self.transaction.sourceAccountId = query.accountId; self.transaction.destinationAccountId = query.accountId; break; @@ -528,16 +569,16 @@ export default { } if (!self.transaction.sourceAccountId) { - self.transaction.sourceAccountId = self.allAccounts[0].id; + self.transaction.sourceAccountId = self.allVisibleAccounts[0].id; } if (!self.transaction.destinationAccountId) { - self.transaction.destinationAccountId = self.allAccounts[0].id; + self.transaction.destinationAccountId = self.allVisibleAccounts[0].id; } } if (query.id) { - const transaction = responses[3].data.result; + const transaction = responses[3]; if (self.mode === 'edit') { self.transaction.id = transaction.id; @@ -578,11 +619,8 @@ export default { }).catch(error => { self.$logger.error('failed to load essential data for editing transaction', error); - if (error.response && error.response.data && error.response.data.errorMessage) { - self.$toast({ error: error.response.data }); - router.back(); - } else if (!error.processed) { - self.$toast('An error has occurred'); + if (!error.processed) { + self.$toast(error.message || error); router.back(); } }); @@ -623,28 +661,15 @@ export default { return; } - let promise = null; - - if (self.mode === 'add') { - promise = self.$services.addTransaction(submitTransaction); - } else if (self.mode === 'edit') { + if (self.mode === 'edit') { submitTransaction.id = self.transaction.id; - promise = self.$services.modifyTransaction(submitTransaction); } - promise.then(response => { + self.$store.dispatch('saveTransaction', { + transaction: submitTransaction + }).then(() => { self.submitting = false; self.$hideLoading(); - const data = response.data; - - if (!data || !data.success || !data.result) { - if (self.mode === 'add') { - self.$toast('Unable to add transaction'); - } else if (self.mode === 'edit') { - self.$toast('Unable to save transaction'); - } - return; - } if (self.mode === 'add') { self.$toast('You have added a new transaction'); @@ -654,19 +679,11 @@ export default { router.back(); }).catch(error => { - self.$logger.error('failed to save transaction', error); - self.submitting = false; self.$hideLoading(); - if (error.response && error.response.data && error.response.data.errorMessage) { - self.$toast({ error: error.response.data }); - } else if (!error.processed) { - if (self.mode === 'add') { - self.$toast('Unable to add transaction'); - } else if (self.mode === 'edit') { - self.$toast('Unable to save transaction'); - } + if (!error.processed) { + self.$toast(error.message || error); } }); }, @@ -706,73 +723,6 @@ export default { } } }, - setAccountsDisplayBalance(accounts) { - for (let i = 0; i < accounts.length; i++) { - const account = accounts[i]; - - if (this.showAccountBalance) { - if (account.isAsset) { - account.displayBalance = this.$options.filters.currency(account.balance, account.currency); - } else if (account.isLiability) { - account.displayBalance = this.$options.filters.currency(-account.balance, account.currency); - } else { - account.displayBalance = this.$options.filters.currency(account.balance, account.currency); - } - } else { - account.displayBalance = '***'; - } - } - }, - setCategoriesDisplayBalance(categorizedAccounts) { - for (let category in categorizedAccounts) { - if (!Object.prototype.hasOwnProperty.call(categorizedAccounts, category)) { - continue; - } - - const accountCategory = categorizedAccounts[category]; - - if (this.showAccountBalance) { - const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category); - let totalBalance = 0; - let hasUnCalculatedAmount = false; - - for (let i = 0; i < accountsBalance.length; i++) { - if (accountsBalance[i].currency === this.defaultCurrency) { - if (accountsBalance[i].isAsset) { - totalBalance += accountsBalance[i].balance; - } else if (accountsBalance[i].isLiability) { - totalBalance -= accountsBalance[i].balance; - } else { - totalBalance += accountsBalance[i].balance; - } - } else { - const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); - - if (!this.$utilities.isNumber(balance)) { - hasUnCalculatedAmount = true; - continue; - } - - if (accountsBalance[i].isAsset) { - totalBalance += Math.floor(balance); - } else if (accountsBalance[i].isLiability) { - totalBalance -= Math.floor(balance); - } else { - totalBalance += Math.floor(balance); - } - } - } - - if (hasUnCalculatedAmount) { - totalBalance = totalBalance + '+'; - } - - accountCategory.displayBalance = this.$options.filters.currency(totalBalance, this.defaultCurrency); - } else { - accountCategory.displayBalance = '***'; - } - } - }, getFontSizeByAmount(amount) { if (amount >= 100000000 || amount <= -100000000) { return 32;