support scheduled transaction (#2)

This commit is contained in:
MaysWind
2024-08-26 01:52:52 +08:00
parent 17d4fec256
commit d2eaf5c6da
42 changed files with 1437 additions and 112 deletions
+18 -1
View File
@@ -67,6 +67,12 @@
<span class="nav-item-title">{{ $t('Transaction Templates') }}</span>
</router-link>
</li>
<li class="nav-link" v-if="isUserScheduledTransactionEnabled">
<router-link to="/schedule/list">
<v-icon class="nav-item-icon" :icon="icons.scheduledTransactions"/>
<span class="nav-item-title">{{ $t('Scheduled Transactions') }}</span>
</router-link>
</li>
<li class="nav-section-title">
<div class="title-wrapper">
<span class="title-text">{{ $t('Miscellaneous') }}</span>
@@ -168,7 +174,7 @@
</div>
<div class="layout-page-content">
<div class="page-content-container">
<router-view/>
<router-view :key="currentRoutePath" />
</div>
</div>
</div>
@@ -188,6 +194,7 @@
<script>
import { useDisplay } from 'vuetify';
import { useTheme } from 'vuetify';
import { useRoute } from 'vue-router';
import { mapStores } from 'pinia';
import { useRootStore } from '@/stores/index.js';
@@ -195,6 +202,7 @@ import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import assetConstants from '@/consts/asset.js';
import { isUserScheduledTransactionEnabled } from '@/lib/server_settings.js';
import { getSystemTheme, setExpenseAndIncomeAmountColor } from '@/lib/ui.js';
import {
@@ -205,6 +213,7 @@ import {
mdiViewDashboardOutline,
mdiTagOutline,
mdiClipboardTextOutline,
mdiClipboardTextClockOutline,
mdiChartPieOutline,
mdiSwapHorizontal,
mdiCogOutline,
@@ -235,6 +244,7 @@ export default {
categories: mdiViewDashboardOutline,
tags: mdiTagOutline,
templates: mdiClipboardTextOutline,
scheduledTransactions: mdiClipboardTextClockOutline,
statistics: mdiChartPieOutline,
exchangeRates: mdiSwapHorizontal,
settings: mdiCogOutline,
@@ -258,6 +268,10 @@ export default {
mdAndDown() {
return this.display.mdAndDown.value;
},
currentRoutePath() {
const route = useRoute();
return route.path;
},
currentNickName() {
return this.userStore.currentUserNickname || this.$t('User');
},
@@ -280,6 +294,9 @@ export default {
}
}
},
isUserScheduledTransactionEnabled() {
return isUserScheduledTransactionEnabled();
},
isEnableApplicationLock() {
return this.settingsStore.appSettings.applicationLock;
}
+22 -7
View File
@@ -4,7 +4,7 @@
<v-card>
<template #title>
<div class="title-and-toolbar d-flex align-center">
<span>{{ $t('Transaction Templates') }}</span>
<span>{{ templateType === allTemplateTypes.Schedule ? $t('Scheduled Transactions') : $t('Transaction Templates') }}</span>
<v-btn class="ml-3" color="default" variant="outlined"
:disabled="loading || updating" @click="add">{{ $t('Add') }}</v-btn>
<v-btn class="ml-3" color="primary" variant="tonal"
@@ -60,7 +60,9 @@
<tbody v-if="!loading && noAvailableTemplate">
<tr>
<td>{{ $t('No available template. Once you add templates, you can quickly add a new transaction using the dropdown menu of the Add button on the transaction list page') }}</td>
<td v-if="templateType === allTemplateTypes.Normal">{{ $t('No available template. Once you add templates, you can quickly add a new transaction using the dropdown menu of the Add button on the transaction list page') }}</td>
<td v-if="templateType === allTemplateTypes.Schedule">{{ $t('No available scheduled transactions') }}</td>
<td v-else>{{ $t('No available template') }}</td>
</tr>
</tbody>
@@ -79,9 +81,9 @@
<v-badge class="right-bottom-icon" color="secondary"
location="bottom right" offset-x="8" :icon="icons.hide"
v-if="element.hidden">
<v-icon size="20" start :icon="icons.text"/>
<v-icon size="20" start :icon="templateType === allTemplateTypes.Schedule ? icons.clock : icons.text"/>
</v-badge>
<v-icon size="20" start :icon="icons.text" v-else-if="!element.hidden"/>
<v-icon size="20" start :icon="templateType === allTemplateTypes.Schedule ? icons.clock : icons.text" v-else-if="!element.hidden"/>
<span class="transaction-template-name">{{ element.name }}</span>
</div>
@@ -162,13 +164,17 @@ import {
mdiDeleteOutline,
mdiDrag,
mdiDotsVertical,
mdiTextBoxOutline
mdiTextBoxOutline,
mdiClockTimeNineOutline
} from '@mdi/js';
export default {
components: {
EditDialog
},
props: [
'initType',
],
data() {
return {
templateType: templateConstants.allTemplateTypes.Normal,
@@ -189,7 +195,8 @@ export default {
remove: mdiDeleteOutline,
drag: mdiDrag,
more: mdiDotsVertical,
text: mdiTextBoxOutline
text: mdiTextBoxOutline,
clock: mdiClockTimeNineOutline
}
};
},
@@ -217,11 +224,15 @@ export default {
}
return count;
},
allTemplateTypes() {
return templateConstants.allTemplateTypes;
}
},
created() {
const self = this;
self.templateType = self.initType;
self.loading = true;
self.transactionTemplatesStore.loadAllTemplates({
@@ -325,6 +336,7 @@ export default {
self.$refs.editDialog.open({
id: template.id,
currentTemplate: {
templateType: template.templateType,
name: template.name,
type: template.type,
categoryId: template.categoryId,
@@ -334,7 +346,10 @@ export default {
destinationAmount: template.destinationAmount,
hideAmount: template.hideAmount,
tagIds: template.tagIds,
comment: template.comment
comment: template.comment,
scheduledFrequencyType: template.scheduledFrequencyType,
scheduledFrequency: template.scheduledFrequency,
utcOffset: template.utcOffset
}
}).then(result => {
if (result && result.message) {
@@ -195,7 +195,15 @@
v-model="transaction.time"
@error="showDateTimeError" />
</v-col>
<v-col cols="12" md="6" v-if="type === 'transaction'">
<v-col cols="12" md="6" v-if="type === 'template' && transaction.templateType === allTemplateTypes.Schedule">
<schedule-frequency-select
:readonly="mode === 'view'"
:disabled="loading || submitting"
:label="$t('Scheduled Transaction Frequency')"
v-model:type="transaction.scheduledFrequencyType"
v-model="transaction.scheduledFrequency" />
</v-col>
<v-col cols="12" md="6" v-if="type === 'transaction' || (type === 'template' && transaction.templateType === allTemplateTypes.Schedule)">
<v-autocomplete
class="transaction-edit-timezone"
item-title="displayNameWithUtcOffset"
@@ -349,6 +357,7 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
import categoryConstants from '@/consts/category.js';
import transactionConstants from '@/consts/transaction.js';
import templateConstants from '@/consts/template.js';
import logger from '@/lib/logger.js';
import {
getNameByKeyValue
@@ -423,12 +432,18 @@ export default {
} else {
return 'Transaction Detail';
}
} else if (this.type === 'template') {
} else if (this.type === 'template' && this.transaction.templateType === templateConstants.allTemplateTypes.Normal) {
if (this.mode === 'add') {
return 'Add Transaction Template';
} else if (this.mode === 'edit') {
return 'Edit Transaction Template';
}
} else if (this.type === 'template' && this.transaction.templateType === templateConstants.allTemplateTypes.Schedule) {
if (this.mode === 'add') {
return 'Add Scheduled Transaction';
} else if (this.mode === 'edit') {
return 'Edit Scheduled Transaction';
}
}
return '';
@@ -497,6 +512,9 @@ export default {
allCategoryTypes() {
return categoryConstants.allCategoryTypes;
},
allTemplateTypes() {
return templateConstants.allTemplateTypes;
},
allTimezones() {
return this.$locale.getAllTimezones(true);
},
@@ -721,11 +739,22 @@ export default {
self.transaction.templateType = options.templateType;
}
if (self.transaction.templateType === templateConstants.allTemplateTypes.Schedule) {
self.transaction.scheduledFrequencyType = templateConstants.allTemplateScheduledFrequencyTypes.Disabled.type;
self.transaction.scheduledFrequency = '';
}
if (options && options.id) {
if (options.currentTemplate) {
self.setTransaction(options.currentTemplate, options, false, false);
self.transaction.templateType = options.currentTemplate.templateType;
self.transaction.name = options.currentTemplate.name;
if (self.transaction.templateType === templateConstants.allTemplateTypes.Schedule) {
self.transaction.scheduledFrequencyType = options.currentTemplate.scheduledFrequencyType;
self.transaction.scheduledFrequency = options.currentTemplate.scheduledFrequency;
self.transaction.utcOffset = options.currentTemplate.utcOffset;
}
}
self.mode = 'edit';
@@ -772,6 +801,12 @@ export default {
self.setTransaction(template, options, false, false);
self.transaction.templateType = template.templateType;
self.transaction.name = template.name;
if (self.transaction.templateType === templateConstants.allTemplateTypes.Schedule) {
self.transaction.scheduledFrequencyType = template.scheduledFrequencyType;
self.transaction.scheduledFrequency = template.scheduledFrequency;
self.transaction.utcOffset = template.utcOffset;
}
} else {
self.setTransaction(null, options, true, true);
}
@@ -48,6 +48,12 @@
count: displayDataStatistics ? displayDataStatistics.totalTransactionTemplateCount : '-',
icon: icons.templates,
color: 'secondary-darken-1'
},
{
title: 'Scheduled Transactions',
count: displayDataStatistics ? displayDataStatistics.totalScheduledTransactionCount : '-',
icon: icons.scheduledTransactions,
color: 'success-darken-1'
}
]">
<div class="d-flex align-center">
@@ -161,6 +167,7 @@ import {
mdiViewDashboardOutline,
mdiTagOutline,
mdiClipboardTextOutline,
mdiClipboardTextClockOutline,
mdiAlert
} from '@mdi/js';
@@ -179,6 +186,7 @@ export default {
categories: mdiViewDashboardOutline,
tags: mdiTagOutline,
templates: mdiClipboardTextOutline,
scheduledTransactions: mdiClipboardTextClockOutline,
alert: mdiAlert
}
}
@@ -197,7 +205,8 @@ export default {
totalAccountCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalAccountCount),
totalTransactionCategoryCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCategoryCount),
totalTransactionTagCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionTagCount),
totalTransactionTemplateCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionTemplateCount)
totalTransactionTemplateCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionTemplateCount),
totalScheduledTransactionCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalScheduledTransactionCount)
};
},
isDataExportingEnabled() {