import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'; import { StateInterface } from 'src/store'; import { axios } from 'src/boot/axios'; import { AxiosResponse } from 'axios'; interface BalanceResponse { balance: number; credit: number; debit: number; } export interface UserBalance extends BalanceResponse { limit: number | null; } export interface BalanceInterface { balances: Map; shortcuts: Array; transactions: Array; loading: number; } export interface StateInterfaceBalance extends StateInterface { balance: BalanceInterface; } const state: BalanceInterface = { balances: new Map(), shortcuts: [], transactions: [], loading: 0 }; function fixTransaction(t: FG.Transaction) { t.time = new Date(t.time); } const mutations: MutationTree = { setBalance(state, data: { userid: string; balance: BalanceResponse }) { state.balances.set( data.userid, Object.assign({ limit: state.balances.get(data.userid)?.limit || null }, data.balance) ); }, changeBalance(state, data: { userid: string; amount: number }) { const user = state.balances.get(data.userid); if (data.amount < 0) user.debit += data.amount; else user.credit += data.amount; user.balance += data.amount; }, setLimit(state, data: { userid: string; limit: number | null }) { if (state.balances.has(data.userid)) (state.balances.get(data.userid)).limit = data.limit; else state.balances.set(data.userid, { balance: 0, debit: 0, credit: 0, limit: data.limit }); }, setLoading(state, data = true) { if (data) state.loading += 1; else state.loading -= 1; }, setShortcuts(state, data: Array) { state.shortcuts.splice(0, state.shortcuts.length, ...data); }, addTransaction(state, data: FG.Transaction) { state.transactions.push(data); }, addTransactions(state, data: [FG.Transaction]) { state.transactions.push(...data); state.transactions.sort((a, b) => (a.time <= b.time ? -1 : 1)); }, reverseTransaction(state, data: { transaction: FG.Transaction; reversal: FG.Transaction }) { const idx = state.transactions.findIndex(value => value.id === data.transaction.id); data.transaction.reversal_id = data.reversal.id; if (idx > -1) state.transactions[idx] = data.transaction; else state.transactions.push(data.transaction); } }; const actions: ActionTree = { addShortcut({ commit, state, rootState }, shortcut) { const sc = [...state.shortcuts, shortcut]; sc.sort(); const user = rootState.user.currentUser; return axios.put(`/users/${user.userid}/balance/shortcuts`, sc).then(() => { commit('setShortcuts', sc); }); }, removeShortcut({ commit, state, rootState }, shortcut) { const sc = state.shortcuts.filter((value: number) => value != shortcut); const user = rootState.user.currentUser; return axios.put(`/users/${user.userid}/balance/shortcuts`, sc).then(() => { commit('setShortcuts', sc); }); }, getShortcuts({ commit, state, rootState }, force = false) { if (force || state.shortcuts.length == 0) { commit('setLoading'); const user = rootState.user.currentUser; return axios .get(`/users/${user.userid}/balance/shortcuts`) .then(({ data }: AxiosResponse) => { commit('setShortcuts', data); return data; }) .finally(() => commit('setLoading', false)); } }, getBalance({ commit, rootState }, user: FG.User | undefined = undefined) { commit('setLoading'); if (!user) user = rootState.user.currentUser; return axios .get(`/users/${user.userid}/balance`) .then(({ data }: AxiosResponse) => { commit('setBalance', { userid: user?.userid, balance: data }); return data; }) .finally(() => commit('setLoading', false)); }, getTransactions( { commit, rootState }, payload: { userid?: string; filter?: { limit?: number; offset?: number; from?: Date; to?: Date }; } ) { commit('setLoading'); if (!payload.userid) payload.userid = (rootState.user.currentUser).userid; if (!payload.filter) payload.filter = { limit: 10 }; return axios .get(`/users/${payload.userid}/balance/transactions`, { params: payload.filter || {} }) .then(({ data }: AxiosResponse<[FG.Transaction]>) => { data.forEach(t => fixTransaction(t)); commit('addTransactions', data); return data; }) .finally(() => commit('setLoading', false)); }, getTransactionsCount( { state }, payload: { userid?: string; filter?: { limit?: number; offset?: number; from?: Date; to?: Date }; } ) { return Promise.resolve(3); }, getLimit({ rootState, commit }) { commit('setLoading'); axios /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */ .get(`/users/${rootState.user.currentUser?.userid}/balance/limit`) .then(({ data }) => { console.log(data); }) .catch(err => { console.warn(err); }) .finally(() => commit('setLoading', false)); }, revert({ dispatch, commit }, transaction: FG.Transaction) { return axios .delete(`/balance/${transaction.id}`) .then(({ data }: AxiosResponse) => { fixTransaction(data); commit('reverseTransaction', { transaction: transaction, reversal: data }); dispatch('getBalance').catch(err => console.warn(err)); }); }, changeBalance({ dispatch, commit }, data: { amount: number; user: string; sender?: string }) { commit('setLoading'); return axios .put(`/users/${data.user}/balance`, data) .then((response: AxiosResponse) => { const transaction = response.data; fixTransaction(transaction); commit('addTransaction', transaction); commit(state.balances.has(data.user) ? 'changeBalance' : 'setBalance', { userid: data.user, amount: data.amount }); if (data.sender) commit(state.balances.has(data.sender) ? 'changeBalance' : 'setBalance', { userid: data.sender, amount: -1 * data.amount }); return transaction; }) .catch(err => { console.debug(err); // Maybe Balance changed void dispatch('getTransactions', {}); return dispatch('getBalance', data.sender ? data.sender : data.user); }) .finally(() => commit('setLoading', false)); } }; const getters: GetterTree = {}; const balance: Module = { namespaced: true, state, mutations, actions, getters }; export default balance;