import transactionConstants from '../consts/transaction.js'; import services from '../lib/services.js'; import logger from '../lib/logger.js'; import utilities from '../lib/utilities/index.js'; import { getExchangedAmount } from './exchangeRates.js'; import { LOAD_TRANSACTION_LIST, INIT_TRANSACTION_LIST_FILTER, UPDATE_TRANSACTION_LIST_FILTER, COLLAPSE_MONTH_IN_TRANSACTION_LIST, SAVE_TRANSACTION_IN_TRANSACTION_LIST, REMOVE_TRANSACTION_FROM_TRANSACTION_LIST, UPDATE_TRANSACTION_LIST_INVALID_STATE, UPDATE_ACCOUNT_LIST_INVALID_STATE, UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, } from './mutations.js'; const emptyTransactionResult = { items: [], transactionsNextTimeId: 0 }; export function initTransactionListFilter(context, filter) { context.commit(INIT_TRANSACTION_LIST_FILTER, filter); } export function updateTransactionListFilter(context, filter) { context.commit(UPDATE_TRANSACTION_LIST_FILTER, filter); } export function loadTransactions(context, { reload, autoExpand, defaultCurrency }) { let actualMaxTime = context.state.transactionsNextTimeId; if (reload && context.state.transactionsFilter.maxTime > 0) { actualMaxTime = context.state.transactionsFilter.maxTime * 1000 + 999; } else if (reload && context.state.transactionsFilter.maxTime <= 0) { actualMaxTime = 0; } return new Promise((resolve, reject) => { services.getTransactions({ maxTime: actualMaxTime, minTime: context.state.transactionsFilter.minTime * 1000, type: context.state.transactionsFilter.type, categoryId: context.state.transactionsFilter.categoryId, accountId: context.state.transactionsFilter.accountId, keyword: context.state.transactionsFilter.keyword }).then(response => { const data = response.data; if (!data || !data.success || !data.result) { if (reload) { context.commit(LOAD_TRANSACTION_LIST, { transactions: emptyTransactionResult, reload: reload, autoExpand: autoExpand, defaultCurrency: defaultCurrency }); if (!context.state.transactionListStateInvalid) { context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, true); } } reject({ message: 'Unable to get transaction list' }); return; } context.commit(LOAD_TRANSACTION_LIST, { transactions: data.result, reload: reload, autoExpand: autoExpand, defaultCurrency: defaultCurrency }); if (reload) { if (context.state.transactionListStateInvalid) { context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, false); } } resolve(data.result); }).catch(error => { logger.error('failed to load transaction list', error); if (reload) { context.commit(LOAD_TRANSACTION_LIST, { transactions: emptyTransactionResult, reload: reload, autoExpand: autoExpand, defaultCurrency: defaultCurrency }); if (!context.state.transactionListStateInvalid) { context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, true); } } 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 list' }); } else { reject(error); } }); }); } export 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; } 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); } }); }); } export function saveTransaction(context, { transaction, defaultCurrency }) { 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; } if (!transaction.id) { if (!context.state.transactionListStateInvalid) { context.commit(UPDATE_TRANSACTION_LIST_INVALID_STATE, true); } } else { context.commit(SAVE_TRANSACTION_IN_TRANSACTION_LIST, { transaction: data.result, defaultCurrency: defaultCurrency }); } if (!context.state.accountListStateInvalid) { context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true); } if (!context.state.transactionOverviewStateInvalid) { context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true); } if (!context.state.transactionStatisticsStateInvalid) { context.commit(UPDATE_TRANSACTION_STATISTICS_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 function deleteTransaction(context, { transaction, defaultCurrency, beforeResolve }) { return new Promise((resolve, reject) => { services.deleteTransaction({ id: transaction.id }).then(response => { const data = response.data; if (!data || !data.success || !data.result) { reject({ message: 'Unable to delete this transaction' }); return; } if (beforeResolve) { beforeResolve(() => { context.commit(REMOVE_TRANSACTION_FROM_TRANSACTION_LIST, { transaction: transaction, defaultCurrency: defaultCurrency }); }); } else { context.commit(REMOVE_TRANSACTION_FROM_TRANSACTION_LIST, { transaction: transaction, defaultCurrency: defaultCurrency }); } if (!context.state.accountListStateInvalid) { context.commit(UPDATE_ACCOUNT_LIST_INVALID_STATE, true); } if (!context.state.transactionOverviewStateInvalid) { context.commit(UPDATE_TRANSACTION_OVERVIEW_INVALID_STATE, true); } if (!context.state.transactionStatisticsStateInvalid) { context.commit(UPDATE_TRANSACTION_STATISTICS_INVALID_STATE, true); } resolve(data.result); }).catch(error => { logger.error('failed to delete transaction', error); if (error.response && error.response.data && error.response.data.errorMessage) { reject({ error: error.response.data }); } else if (!error.processed) { reject({ message: 'Unable to delete this transaction' }); } else { reject(error); } }); }); } export function collapseMonthInTransactionList(context, { month, collapse }) { context.commit(COLLAPSE_MONTH_IN_TRANSACTION_LIST, { month: month, collapse: collapse }); } export function noTransaction(state) { for (let i = 0; i < state.transactions.length; i++) { const transactionMonthList = state.transactions[i]; for (let j = 0; j < transactionMonthList.items.length; j++) { if (transactionMonthList.items[j]) { return false; } } } return true; } export function hasMoreTransaction(state) { return state.transactionsNextTimeId > 0; } export function fillTransactionObject(state, transaction, currentUtcOffset) { if (!transaction) { return; } const transactionTime = utilities.parseDateFromUnixTime(transaction.time, transaction.utcOffset, currentUtcOffset); transaction.day = utilities.getDay(transactionTime); transaction.dayOfWeek = utilities.getDayOfWeekName(transactionTime); if (transaction.sourceAccountId) { transaction.sourceAccount = state.allAccountsMap[transaction.sourceAccountId]; } if (transaction.destinationAccountId) { transaction.destinationAccount = state.allAccountsMap[transaction.destinationAccountId]; } if (transaction.categoryId) { transaction.category = state.allTransactionCategoriesMap[transaction.categoryId]; } return transaction; } export function calculateMonthTotalAmount(state, transactionMonthList, defaultCurrency, accountId, incomplete) { if (!transactionMonthList) { return; } let totalExpense = 0; let totalIncome = 0; let hasUnCalculatedTotalExpense = false; let hasUnCalculatedTotalIncome = false; for (let i = 0; i < transactionMonthList.items.length; i++) { const transaction = transactionMonthList.items[i]; let amount = transaction.sourceAmount; let account = transaction.sourceAccount; if (accountId && transaction.destinationAccount && (transaction.destinationAccount.id === accountId || transaction.destinationAccount.parentId === accountId)) { amount = transaction.destinationAmount; account = transaction.destinationAccount; } if (!account) { continue; } if (account.currency !== defaultCurrency) { const balance = getExchangedAmount(state)(amount, account.currency, defaultCurrency); if (!utilities.isNumber(balance)) { if (transaction.type === transactionConstants.allTransactionTypes.Expense) { hasUnCalculatedTotalExpense = true; } else if (transaction.type === transactionConstants.allTransactionTypes.Income) { hasUnCalculatedTotalIncome = true; } continue; } amount = Math.floor(balance); } if (transaction.type === transactionConstants.allTransactionTypes.Expense) { totalExpense += amount; } else if (transaction.type === transactionConstants.allTransactionTypes.Income) { totalIncome += amount; } else if (transaction.type === transactionConstants.allTransactionTypes.Transfer && accountId) { if (accountId === transaction.sourceAccountId) { totalExpense += amount; } else if (accountId === transaction.destinationAccountId) { totalIncome += amount; } else if (transaction.sourceAccount && accountId === transaction.sourceAccount.parentId && transaction.destinationAccount && accountId === transaction.destinationAccount.parentId) { // Do Nothing } else if (transaction.sourceAccount && accountId === transaction.sourceAccount.parentId) { totalExpense += amount; } else if (transaction.destinationAccount && accountId === transaction.destinationAccount.parentId) { totalIncome += amount; } } } transactionMonthList.totalAmount = { expense: totalExpense, incompleteExpense: incomplete || hasUnCalculatedTotalExpense, income: totalIncome, incompleteIncome: incomplete || hasUnCalculatedTotalIncome }; }