release v2.0.0 #4
|
@ -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,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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 };
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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];
|
||||||
|
|
Loading…
Reference in New Issue