[pinia] Implemented and migrated balance

* Fixed revert of transaction if Conflic occures
This commit is contained in:
Ferdinand Thiessen 2021-02-04 02:44:20 +01:00
parent 4a7ed50281
commit fd45a46c01
11 changed files with 294 additions and 254 deletions

View File

@ -1,10 +1,6 @@
<template> <template>
<q-card> <q-card>
<BalanceHeader <BalanceHeader v-model="user" :show-selector="showSelector" @open-history="openHistory" />
:show-selector="showSelector"
@update:user="userUpdated"
@open-history="openHistory"
/>
<q-separator /> <q-separator />
<q-card-section v-if="shortCuts" class="row q-col-gutter-md"> <q-card-section v-if="shortCuts" class="row q-col-gutter-md">
@ -63,53 +59,39 @@
<script lang="ts"> <script lang="ts">
import { computed, ref, defineComponent, onBeforeMount } from 'vue'; import { computed, ref, defineComponent, onBeforeMount } from 'vue';
import { hasPermission } from 'src/utils/permission'; import { hasPermission } from 'src/utils/permission';
import { Store, useStore, mapGetters } from 'vuex';
import BalanceHeader from '../components/BalanceHeader.vue'; import BalanceHeader from '../components/BalanceHeader.vue';
import PERMISSIONS from '../permissions'; import PERMISSIONS from '../permissions';
import { StateInterface } from 'src/store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store';
export default defineComponent({ export default defineComponent({
name: 'BalanceAdd', name: 'BalanceAdd',
components: { BalanceHeader }, components: { BalanceHeader },
emits: { 'open-history': () => true }, emits: { 'open-history': () => true },
setup(_, { emit }) { setup(_, { emit }) {
const store = useBalanceStore();
const mainStore = useMainStore();
onBeforeMount(() => { onBeforeMount(() => {
void store.dispatch('balance/getShortcuts'); void store.getShortcuts();
if ((<FG.Transaction[]>balanceGatters.transactions())?.length == 0)
// No transaction, load at most six since yesterday
void store.dispatch('balance/getTransactions', {
filter: { limit: 6, from: new Date(new Date().setDate(new Date().getDate() - 1)) },
});
}); });
//const store = <Store<StateInterfaceBalance>>root.$store;
const store = useStore<Store<StateInterface>>();
const userGetters = mapGetters('users', ['currentUser', 'roles', 'users']);
const balanceGatters = mapGetters('balance', ['balances', 'transactions', 'shortcuts']);
const amount = ref<number>(0); const amount = ref<number>(0);
const showAddShortcut = ref(false); const showAddShortcut = ref(false);
const user = ref(<FG.User>userGetters.currentUser()); const user = ref(mainStore.currentUser);
//const shortCuts = ref(balanceState.shortcuts); const shortCuts = computed(() => store.shortcuts);
const shortCuts = computed(() => <number[]>balanceGatters.shortcuts());
const canAddCredit = computed(() => hasPermission(PERMISSIONS.CREDIT)); const canAddCredit = hasPermission(PERMISSIONS.CREDIT);
const showSelector = computed( const showSelector = hasPermission(PERMISSIONS.DEBIT) || hasPermission(PERMISSIONS.CREDIT);
() => hasPermission(PERMISSIONS.DEBIT) || hasPermission(PERMISSIONS.CREDIT)
);
function addShortcut() { function addShortcut() {
if (amount.value != 0) void store.dispatch('balance/addShortcut', amount.value * -1); if (amount.value != 0) void store.createShortcut(amount.value * -1);
} }
function removeShortcut(shortcut: number) { function removeShortcut(shortcut: number) {
void store.dispatch('balance/removeShortcut', shortcut); void store.removeShortcut(shortcut);
} }
function userUpdated(selectedUser: FG.User) { async function changeBalance(amount: number) {
user.value = selectedUser; await store.changeBalance(amount, user.value);
}
function changeBalance(amount: number) {
store
.dispatch('balance/changeBalance', { amount: amount, user: user.value?.userid })
.catch((err) => console.log(err));
} }
function openHistory() { function openHistory() {
@ -126,7 +108,6 @@ export default defineComponent({
amount, amount,
showSelector, showSelector,
shortCuts, shortCuts,
userUpdated,
openHistory, openHistory,
}; };
}, },

View File

@ -1,11 +1,14 @@
<template> <template>
<q-card-section class="fit row justify-left content-center items-center q-col-gutter-sm"> <q-card-section class="fit row justify-left content-center items-center q-col-gutter-sm">
<div class="text-h6 col-5"> <div class="col-5">
Aktueller Stand: {{ balance.balance.toFixed(2) }} <div v-if="balance" class="text-h6">
<q-badge v-if="isLocked" color="negative" align="top"> gesperrt </q-badge> Aktueller Stand: {{ balance.balance.toFixed(2) }}
<q-badge v-if="isLocked" color="negative" align="top"> gesperrt </q-badge>
</div>
<q-spinner v-else color="primary" size="3em" />
</div> </div>
<div v-if="showSelector" class="col-6"> <div v-if="showSelector" class="col-6">
<UserSelector :user="user" @update:user="userUpdated" /> <UserSelector v-model="user" />
</div> </div>
<div class="col-1 justify-end"> <div class="col-1 justify-end">
<q-btn round flat icon="mdi-format-list-checks" @click="openHistory" /> <q-btn round flat icon="mdi-format-list-checks" @click="openHistory" />
@ -14,46 +17,50 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, computed, defineComponent, onBeforeMount } from 'vue'; import { computed, defineComponent, onBeforeMount, PropType } from 'vue';
import UserSelector from 'src/plugins/user/components/UserSelector.vue'; import UserSelector from 'src/plugins/user/components/UserSelector.vue';
import { StateInterfaceBalance, UserBalance } from '../store/balance'; import { useBalanceStore } from '../store';
import { useStore, mapGetters } from 'vuex'; import { useMainStore } from 'src/store';
export default defineComponent({ export default defineComponent({
name: 'BalanceHeader', name: 'BalanceHeader',
components: { UserSelector }, components: { UserSelector },
props: { showSelector: Boolean }, props: {
emits: { 'update:user': (u: FG.User) => !!u, 'open-history': () => true }, showSelector: Boolean,
setup(_, { emit }) { modelValue: {
onBeforeMount(() => void store.dispatch('balance/getBalance')); required: true,
const store = useStore<StateInterfaceBalance>(); type: Object as PropType<FG.User>,
const userGetters = mapGetters('users', ['currentUser']); },
const balanceGetters = mapGetters('balance', ['balances']); },
const user = ref(<FG.User>userGetters.currentUser()); emits: { 'update:modelValue': (u: FG.User) => !!u, 'open-history': () => true },
const balance = computed(() => { setup(props, { emit }) {
return ( const store = useBalanceStore();
(<Map<string, UserBalance>>balanceGetters.balances()).get(user.value.userid) || { const mainStore = useMainStore();
balance: 0,
limit: null,
}
);
});
const isLocked = computed( onBeforeMount(() => void store.getBalance(mainStore.currentUser));
() => balance.value.limit !== null && balance.value.balance >= balance.value.limit
const balance = computed(() =>
store.balances.find((x) => x.userid === props.modelValue.userid)
); );
function userUpdated(selectedUser: FG.User) { const isLocked = computed(
void store.dispatch('balance/getBalance', selectedUser); () =>
user.value = selectedUser; balance.value === undefined ||
emit('update:user', selectedUser); (balance.value.limit !== undefined && balance.value.balance <= balance.value.limit)
} );
const user = computed({
get: () => props.modelValue,
set: (x: FG.User) => {
void store.getBalance(x);
emit('update:modelValue', x);
},
});
function openHistory() { function openHistory() {
emit('open-history'); emit('open-history');
} }
return { user, balance, isLocked, userUpdated, openHistory }; return { user, balance, isLocked, openHistory };
}, },
}); });
</script> </script>

View File

@ -1,17 +1,13 @@
<template> <template>
<q-card> <q-card>
<BalanceHeader <BalanceHeader v-model="sender" :show-selector="showSelector" @open-history="openHistory" />
:show-selector="showSelector"
@update:user="senderUpdated"
@open-history="openHistory"
/>
<q-separator /> <q-separator />
<q-card-section class="row q-col-gutter-md items-center"> <q-card-section class="row q-col-gutter-md items-center">
<div class="col-sm-4 col-xs-12"> <div class="col-sm-4 col-xs-12">
<q-input v-model.number="amount" type="number" filled label="Betrag" step="0.1" min="0" /> <q-input v-model.number="amount" type="number" filled label="Betrag" step="0.1" min="0" />
</div> </div>
<div class="col-sm-4 col-xs-6"> <div class="col-sm-4 col-xs-6">
<UserSelector :user="receiver" label="Empfänger" @update:user="receiverUpdated" /> <UserSelector v-model="receiver" label="Empfänger" />
</div> </div>
<div class="col-sm-4 col-xs-6"> <div class="col-sm-4 col-xs-6">
<q-btn <q-btn
@ -29,22 +25,22 @@
<script lang="ts"> <script lang="ts">
import { computed, ref, defineComponent } from 'vue'; import { computed, ref, defineComponent } from 'vue';
import { hasPermission } from 'src/utils/permission'; import { hasPermission } from 'src/utils/permission';
import { mapGetters, Store, useStore } from 'vuex';
import UserSelector from 'src/plugins/user/components/UserSelector.vue'; import UserSelector from 'src/plugins/user/components/UserSelector.vue';
import BalanceHeader from '../components/BalanceHeader.vue'; import BalanceHeader from '../components/BalanceHeader.vue';
import PERMISSIONS from '../permissions'; import PERMISSIONS from '../permissions';
import { StateInterface } from 'src/store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store';
export default defineComponent({ export default defineComponent({
name: 'BalanceTransfer', name: 'BalanceTransfer',
components: { BalanceHeader, UserSelector }, components: { BalanceHeader, UserSelector },
emits: { 'open-history': () => true }, emits: { 'open-history': () => true },
setup(_, { emit }) { setup(_, { emit }) {
const store = useStore<Store<StateInterface>>(); const store = useBalanceStore();
const userGetters = mapGetters('users', ['currentUser']); const mainStore = useMainStore();
const showSelector = computed(() => hasPermission(PERMISSIONS.SEND_OTHER)); const showSelector = computed(() => hasPermission(PERMISSIONS.SEND_OTHER));
const sender = ref(<FG.User>userGetters.currentUser()); const sender = ref<FG.User | undefined>(mainStore.currentUser);
const receiver = ref<FG.User | undefined>(undefined); const receiver = ref<FG.User | undefined>(undefined);
const amount = ref<number>(0); const amount = ref<number>(0);
@ -57,22 +53,8 @@ export default defineComponent({
); );
}); });
function senderUpdated(selectedUser: FG.User) { async function sendAmount() {
console.log(selectedUser); if (receiver.value) await store.changeBalance(amount.value, receiver.value, sender.value);
sender.value = selectedUser;
}
function receiverUpdated(selectedUser: FG.User) {
receiver.value = selectedUser;
}
function sendAmount() {
store
.dispatch('balance/changeBalance', {
amount: amount.value,
sender: sender.value?.userid,
user: receiver.value?.userid,
})
.catch((err) => console.log(err));
} }
function openHistory() { function openHistory() {
@ -85,8 +67,6 @@ export default defineComponent({
amount, amount,
sendAmount, sendAmount,
showSelector, showSelector,
senderUpdated,
receiverUpdated,
sendDisabled, sendDisabled,
openHistory, openHistory,
}; };

View File

@ -33,8 +33,9 @@
import { ref, computed, defineComponent, onUnmounted, onMounted, PropType } from 'vue'; import { ref, computed, defineComponent, onUnmounted, onMounted, PropType } from 'vue';
import { hasPermission } from 'src/utils/permission'; import { hasPermission } from 'src/utils/permission';
import { formatDateTime } from 'src/utils/datetime'; import { formatDateTime } from 'src/utils/datetime';
import { mapGetters, Store, useStore } from 'vuex'; import { useMainStore } from 'src/store';
import { StateInterface } from 'src/store'; import { useUserStore } from 'src/plugins/user/store';
import { useBalanceStore } from '../store';
export default defineComponent({ export default defineComponent({
name: 'Transaction', name: 'Transaction',
@ -46,33 +47,29 @@ export default defineComponent({
}, },
emits: { 'update:transaction': (t: FG.Transaction) => !!t }, emits: { 'update:transaction': (t: FG.Transaction) => !!t },
setup(props, { emit }) { setup(props, { emit }) {
const mainStore = useMainStore();
const userStore = useUserStore();
const balanceStore = useBalanceStore();
const now = ref(Date.now()); const now = ref(Date.now());
const ival = setInterval(() => (now.value = Date.now()), 1000); const ival = setInterval(() => (now.value = Date.now()), 1000);
const store = useStore<Store<StateInterface>>();
const userGetters = mapGetters('users', ['currentUser']);
const text = ref(''); const text = ref('');
onUnmounted(() => clearInterval(ival)); onUnmounted(() => clearInterval(ival));
onMounted(() => refreshText()); onMounted(() => refreshText());
const isNegative = () => const isNegative = () => props.transaction.sender_id === mainStore.currentUser.userid;
props.transaction.sender_id === (<FG.User>userGetters.currentUser())?.userid;
const refreshText = async () => { const refreshText = async () => {
if (isNegative()) { if (isNegative()) {
text.value = 'Anschreiben'; text.value = 'Anschreiben';
if (props.transaction.receiver_id !== null) { if (props.transaction.receiver_id) {
const user = <FG.User>await store.dispatch('user/getUser', { const user = <FG.User>await userStore.getUser(props.transaction.receiver_id);
userid: props.transaction.receiver_id,
});
text.value = `Gesendet an ${user.display_name}`; text.value = `Gesendet an ${user.display_name}`;
} }
} else { } else {
text.value = 'Gutschrift'; text.value = 'Gutschrift';
if (props.transaction.sender_id !== null) { if (props.transaction.sender_id) {
const user = <FG.User>await store.dispatch('user/getUser', { const user = <FG.User>await userStore.getUser(props.transaction.sender_id);
userid: props.transaction.sender_id,
});
text.value = `Bekommen von ${user.display_name}`; text.value = `Bekommen von ${user.display_name}`;
} }
} }
@ -86,18 +83,15 @@ export default defineComponent({
() => () =>
!isReversed.value && !isReversed.value &&
(hasPermission('balance_reversal') || (hasPermission('balance_reversal') ||
(props.transaction.sender_id === (<FG.User>userGetters.currentUser())?.userid && (props.transaction.sender_id === mainStore.currentUser.userid &&
now.value - props.transaction.time.getTime() < 10000)) now.value - props.transaction.time.getTime() < 10000))
); );
function reverse() { function reverse() {
if (canReverse.value) if (canReverse.value)
store void balanceStore.revert(props.transaction).then(() => {
.dispatch('balance/revert', props.transaction) emit('update:transaction', props.transaction);
.then(() => { });
emit('update:transaction', props.transaction);
})
.catch((error) => console.log(error));
} }
const timeStr = computed(() => { const timeStr = computed(() => {

View File

@ -7,30 +7,21 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useMainStore } from 'src/store';
import { useBalanceStore } from '../store';
import { computed, defineComponent, onBeforeMount } from 'vue'; import { computed, defineComponent, onBeforeMount } from 'vue';
import { UserBalance } from 'src/plugins/balance/store/balance';
import { mapGetters, useStore } from 'vuex';
import { StateInterface } from 'src/store';
export default defineComponent({ export default defineComponent({
name: 'BalanceWidget', name: 'BalanceWidget',
setup() { setup() {
const store = useStore<StateInterface>(); const store = useBalanceStore();
const balanceGetters = mapGetters('balance', ['balances']);
const userGetters = mapGetters('users', ['currentUser']);
onBeforeMount(() => { onBeforeMount(() => {
store.dispatch('balance/getBalance').catch((err) => { const mainStore = useMainStore();
console.warn(err); void store.getBalance(mainStore.currentUser);
});
}); });
const balance = computed( const balance = computed(() => store.balance?.balance || NaN);
() =>
(<Map<string, UserBalance>>balanceGetters.balances()).get(
(<FG.User>userGetters.currentUser()).userid
)?.balance || NaN
);
return { balance }; return { balance };
}, },

View File

@ -14,21 +14,16 @@
// TODO: Fill usefull data // TODO: Fill usefull data
import { ref, defineComponent, onMounted } from 'vue'; import { ref, defineComponent, onMounted } from 'vue';
import { BalancesResponse } from '../store/balance'; import { useBalanceStore } from '../store';
import { useStore } from 'vuex';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'
setup() { setup() {
const store = useStore(); const store = useBalanceStore();
onMounted( onMounted(() => void store.getBalances().then((balances) => rows.value.push(...balances)));
() =>
void store
.dispatch('balance/getBalances')
.then((balances: Array<BalancesResponse>) => rows.value.push(...balances))
);
const rows = ref(new Array<BalancesResponse>()); const rows = ref(store.balances);
const columns = [ const columns = [
{ {

View File

@ -67,29 +67,30 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref, onMounted } from 'vue'; import { computed, defineComponent, ref, onMounted } from 'vue';
import { mapGetters, Store, useStore } from 'vuex';
import { hasSomePermissions } from 'src/utils/permission'; import { hasSomePermissions } from 'src/utils/permission';
import PERMISSIONS from '../permissions'; import PERMISSIONS from '../permissions';
import BalanceAdd from '../components/BalanceAdd.vue'; import BalanceAdd from '../components/BalanceAdd.vue';
import BalanceTransfer from '../components/BalanceTransfer.vue'; import BalanceTransfer from '../components/BalanceTransfer.vue';
import Transaction from '../components/Transaction.vue'; import Transaction from '../components/Transaction.vue';
import { StateInterface } from 'src/store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store';
export default defineComponent({ export default defineComponent({
name: 'BalanceManage', name: 'BalanceManage',
components: { BalanceAdd, BalanceTransfer, Transaction }, components: { BalanceAdd, BalanceTransfer, Transaction },
setup() { setup() {
//const store = <Store<StateInterfaceBalance>>root.$store; const balanceStore = useBalanceStore();
const store = useStore<Store<StateInterface>>(); const mainStore = useMainStore();
const balanceGetters = mapGetters('balance', ['balances', 'transactions']);
const now = new Date(); const now = new Date();
onMounted(() => { onMounted(() => {
void store.dispatch('balance/getTransactions', { void balanceStore.getTransactions(mainStore.currentUser, {
filter: { from: new Date(now.getFullYear(), now.getMonth(), now.getDate()) }, from: new Date(now.getFullYear(), now.getMonth(), now.getDate()),
}); });
}); });
const transactions = computed(() => { const transactions = computed(() => {
return (<FG.Transaction[]>balanceGetters.transactions()) return balanceStore.transactions
.filter((t) => t.original_id == undefined) .filter((t) => t.original_id == undefined)
.filter((t) => t.time > new Date(now.getFullYear(), now.getMonth(), now.getDate())) .filter((t) => t.time > new Date(now.getFullYear(), now.getMonth(), now.getDate()))
.sort((a, b) => (a.time >= b.time ? -1 : 1)); .sort((a, b) => (a.time >= b.time ? -1 : 1));

View File

@ -33,22 +33,21 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onMounted, ref } from 'vue'; import { computed, defineComponent, onMounted, ref } from 'vue';
import { TransactionsResponse, UserBalance } from '../store/balance';
import { mapGetters, Store, useStore } from 'vuex';
import { formatDateTime } from 'src/utils/datetime'; import { formatDateTime } from 'src/utils/datetime';
import { StateInterface } from 'src/store'; import { useBalanceStore } from '../store';
import { useMainStore } from 'src/store';
import { useUserStore } from 'src/plugins/user/store';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'
setup() { setup() {
//const store = <Store<StateInterfaceBalance>>root.$store; const store = useBalanceStore();
const store = useStore<Store<StateInterface>>(); const mainStore = useMainStore();
const userGetters = mapGetters('users', ['currentUser', 'users']); const userStore = useUserStore();
const balanceGetters = mapGetters('balace', ['balances', 'transactions']);
onMounted(() => { onMounted(() => {
void store.dispatch('balance/getBalance'); void store.getBalance(mainStore.currentUser);
void store.dispatch('user/getUsers').then(() => void userStore.getUsers().then(() =>
onRequest({ onRequest({
pagination: pagination.value, pagination: pagination.value,
filter: undefined, filter: undefined,
@ -75,7 +74,7 @@ export default defineComponent({
rowsNumber: number; rowsNumber: number;
} }
function onRequest(props: { pagination: PaginationInterface; filter?: string }) { async function onRequest(props: { pagination: PaginationInterface; filter?: string }) {
const { page, rowsPerPage, sortBy, descending } = props.pagination; const { page, rowsPerPage, sortBy, descending } = props.pagination;
loading.value = true; loading.value = true;
@ -83,34 +82,28 @@ export default defineComponent({
const fetchCount = rowsPerPage === 0 ? pagination.value.rowsNumber : rowsPerPage; const fetchCount = rowsPerPage === 0 ? pagination.value.rowsNumber : rowsPerPage;
// calculate starting row of data // calculate starting row of data
const startRow = (page - 1) * rowsPerPage; const startRow = (page - 1) * rowsPerPage;
store try {
.dispatch('balance/getTransactions', { const result = await store.getTransactions(mainStore.currentUser, {
filter: { offset: startRow,
offset: startRow, limit: fetchCount,
limit: fetchCount, showCancelled: showCancelled.value,
showCancelled: showCancelled.value, showReversals: false,
showReversals: false, });
}, // clear out existing data and add new
}) data.value.splice(0, data.value.length, ...result.transactions);
.then((result: TransactionsResponse) => { // don't forget to update local pagination object
// clear out existing data and add new pagination.value.page = page;
data.value.splice(0, data.value.length, ...result.transactions); pagination.value.rowsPerPage = rowsPerPage;
// don't forget to update local pagination object pagination.value.sortBy = sortBy;
pagination.value.page = page; pagination.value.descending = descending;
pagination.value.rowsPerPage = rowsPerPage; if (result.count) pagination.value.rowsNumber = result.count;
pagination.value.sortBy = sortBy; } catch (error) {
pagination.value.descending = descending; // ...
if (result.count) pagination.value.rowsNumber = result.count; }
}) loading.value = false;
.finally(() => (loading.value = false));
} }
const balance = computed( const balance = computed(() => store.balance?.balance || NaN);
() =>
(<Map<string, UserBalance>>balanceGetters.balances()).get(
(<FG.User>userGetters.currentUser()).userid
)?.balance || NaN
);
const columns = [ const columns = [
{ {
@ -144,8 +137,7 @@ export default defineComponent({
else { else {
if (row.receiver_id == null) return 'Angeschrieben'; if (row.receiver_id == null) return 'Angeschrieben';
else { else {
if (row.receiver_id === (<FG.User>userGetters.currentUser())?.userid) if (row.receiver_id === mainStore.currentUser.userid) return 'Bekommen von X';
return 'Bekommen von X';
else return 'Gesendet an X'; else return 'Gesendet an X';
} }
} }
@ -162,7 +154,7 @@ export default defineComponent({
label: 'Benutzer', label: 'Benutzer',
field: 'author_id', field: 'author_id',
format: (val: string) => { format: (val: string) => {
const user = (<FG.User[]>userGetters.users()).filter((x) => x.userid == val); const user = userStore.users.filter((x) => x.userid == val);
if (user.length > 0) return user[0].display_name; if (user.length > 0) return user[0].display_name;
else return val; else return val;
}, },

View File

@ -0,0 +1,166 @@
import { api } from 'src/boot/axios';
interface BalanceResponse {
balance: number;
credit: number;
debit: number;
limit?: number;
}
export interface BalancesResponse extends BalanceResponse {
userid: string;
}
export interface TransactionsResponse {
transactions: Array<FG.Transaction>;
count?: number;
}
import { defineStore } from 'pinia';
import { useMainStore } from 'src/store';
import { AxiosResponse } from 'axios';
import { Notify } from 'quasar';
function fixTransaction(t: FG.Transaction) {
t.time = new Date(t.time);
}
export const useBalanceStore = defineStore({
id: 'balance',
state: () => ({
balances: [] as BalancesResponse[],
shortcuts: [] as number[],
transactions: [] as FG.Transaction[],
_balances_dirty: 0,
}),
getters: {
balance() {
const mainStore = useMainStore();
return this.balances.find((v) => v.userid === mainStore.user?.userid);
},
},
actions: {
async createShortcut(shortcut: number) {
const mainStore = useMainStore();
this.shortcuts.push(shortcut);
this.shortcuts.sort((a, b) => a - b);
await api.put(`/users/${mainStore.currentUser.userid}/balance/shortcuts`, this.shortcuts);
},
async removeShortcut(shortcut: number) {
const mainStore = useMainStore();
this.shortcuts = this.shortcuts.filter((value: number) => value !== shortcut);
this.shortcuts.sort((a, b) => a - b);
await api.put(`/users/${mainStore.currentUser.userid}/balance/shortcuts`, this.shortcuts);
},
async getShortcuts(force = false) {
if (force || this.shortcuts.length == 0) {
const mainStore = useMainStore();
const { data } = await api.get<number[]>(
`/users/${mainStore.currentUser.userid}/balance/shortcuts`
);
this.shortcuts = data;
}
},
async getBalance(user: FG.User) {
const { data } = await api.get<BalanceResponse>(`/users/${user.userid}/balance`);
const idx = this.balances.findIndex((x) => x.userid === user.userid);
if (idx == -1) this.balances.push(Object.assign(data, { userid: user.userid }));
else this.balances[idx] = Object.assign(data, { userid: user.userid });
return data;
},
async getBalances(force = false) {
if (
force ||
this.balances.length == 0 ||
new Date().getTime() - this._balances_dirty > 60000
) {
const { data } = await api.get<BalancesResponse[]>('/balance');
this.balances = data;
}
return this.balances;
},
async changeBalance(amount: number, user: FG.User, sender: FG.User | undefined = undefined) {
const mainStore = useMainStore();
try {
const { data } = await api.put<FG.Transaction>(`/users/${user.userid}/balance`, {
amount,
user: user.userid,
sender: sender?.userid,
});
fixTransaction(data);
if (
user.userid === mainStore.currentUser.userid ||
sender?.userid === mainStore.currentUser.userid
)
this.transactions.push(data);
const f = this.balances.find((x) => x.userid === user.userid);
if (f) f.balance += amount;
if (sender) {
const f = this.balances.find((x) => x.userid === sender.userid);
if (f) f.balance += -1 * amount;
}
this._balances_dirty = 0;
return data;
} catch ({ response }) {
// Maybe Balance changed
if (response && (<AxiosResponse>response).status == 409) {
Notify.create({
type: 'negative',
group: false,
message: 'Das Limit wurde überschritten!',
timeout: 10000,
progress: true,
actions: [{ icon: 'mdi-close', color: 'white' }],
});
//void this.getTransactions(true);
void this.getBalance(sender ? sender : user);
}
}
},
async getTransactions(
user: FG.User,
filter:
| {
limit?: number;
offset?: number;
from?: Date;
to?: Date;
showReversals?: boolean;
showCancelled?: boolean;
}
| undefined = undefined
) {
if (!filter) filter = { limit: 10 };
const { data } = await api.get<TransactionsResponse>(
`/users/${user.userid}/balance/transactions`,
{ params: filter }
);
data.transactions.forEach((t) => fixTransaction(t));
if (data.transactions) this.transactions.push(...data.transactions);
return data;
},
async revert(transaction: FG.Transaction) {
try {
const { data } = await api.delete<FG.Transaction>(`/balance/${transaction.id}`);
fixTransaction(data);
const f = this.transactions.find((x) => x.id === transaction.id);
if (f) f.reversal_id = data.id;
this.transactions.push(data);
console.log(data);
} catch (error) {
// ...
}
this._balances_dirty = 0;
},
},
});

View File

@ -1,66 +0,0 @@
import { defineStore } from 'pinia';
import { api } from 'src/boot/axios';
import { AxiosResponse } from 'axios';
import { useMainStore } from 'src/store';
interface BalanceResponse {
balance: number;
credit: number;
debit: number;
}
export interface BalancesResponse extends BalanceResponse {
userid: string;
}
export interface TransactionsResponse {
transactions: Array<FG.Transaction>;
count?: number;
}
function fixTransaction(t: FG.Transaction) {
t.time = new Date(t.time);
}
export const balanceStore = defineStore({
id: 'balance',
state: () => ({
balances: [] as BalancesResponse[],
shortcuts: [] as number[],
}),
getters: {
balance() {
const mainStore = useMainStore();
return this.balances.find((v) => v.userid === mainStore.user?.userid);
},
},
actions: {
async createShortcut(shortcut: number) {
const mainStore = useMainStore();
const sc = [...this.shortcuts, shortcut];
sc.sort();
await api.put(`/users/${mainStore.currentUser.userid}/balance/shortcuts`, sc);
this.shortcuts = sc;
},
async removeShortcut(shortcut: number) {
const mainStore = useMainStore();
const sc = this.shortcuts.filter((value: number) => value !== shortcut);
await api.put(`/users/${mainStore.currentUser.userid}/balance/shortcuts`, sc);
this.shortcuts = sc;
},
async getShortcuts(force = false) {
if (force || this.shortcuts.length == 0) {
const mainStore = useMainStore();
const { data } = await api.get<number[]>(
`/users/${mainStore.currentUser.userid}/balance/shortcuts`
);
this.shortcuts = data;
}
},
},
});

View File

@ -33,7 +33,6 @@ export const useUserStore = defineStore({
} catch (error) { } catch (error) {
if (!error || !('response' in error) || (<AxiosError>error).response?.status !== 404) if (!error || !('response' in error) || (<AxiosError>error).response?.status !== 404)
throw error; throw error;
return null;
} }
} else { } else {
return this.users[idx]; return this.users[idx];