support batch update accounts for transactions

This commit is contained in:
MaysWind
2026-04-25 23:17:33 +08:00
parent e38ba2ea0a
commit bab7a0041b
27 changed files with 461 additions and 0 deletions
+6
View File
@@ -66,6 +66,7 @@ import type {
TransactionCreateRequest,
TransactionModifyRequest,
TransactionBatchUpdateCategoryRequest,
TransactionBatchUpdateAccountRequest,
TransactionMoveBetweenAccountsRequest,
TransactionDeleteRequest,
TransactionBatchDeleteRequest,
@@ -619,6 +620,11 @@ export default {
timeout: DEFAULT_BATCH_UPDATE_TRANSACTIONS_API_TIMEOUT
} as ApiRequestConfig);
},
batchUpdateTransactionAccounts: (req: TransactionBatchUpdateAccountRequest): ApiResponsePromise<boolean> => {
return axios.post<ApiResponse<boolean>>('v1/transactions/batch_update/account.json', req, {
timeout: DEFAULT_BATCH_UPDATE_TRANSACTIONS_API_TIMEOUT
} as ApiRequestConfig);
},
moveAllTransactionsBetweenAccounts: (req: TransactionMoveBetweenAccountsRequest): ApiResponsePromise<boolean> => {
return axios.post<ApiResponse<boolean>>('v1/transactions/move/all.json', req);
},
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Kontoliste",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Account List",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Lista de Cuentas",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Liste des comptes",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Elenco account",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "口座リスト",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "ಖಾತೆಗಳ ಪಟ್ಟಿ",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "계좌 목록",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Rekeningenlijst",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Lista de Contas",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Список счетов",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Seznam računov",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "கணக்குகளின் பட்டியல்",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "รายการบัญชี",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Hesap Listesi",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Список рахунків",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "Update Categories for Income Transactions",
"Update Categories for Transfer Transactions": "Update Categories for Transfer Transactions",
"Unable to update categories for transactions": "Unable to update categories for transactions",
"Update Accounts for Transactions": "Update Accounts for Transactions",
"Update Destination Accounts for Transactions": "Update Destination Accounts for Transactions",
"Unable to update accounts for transactions": "Unable to update accounts for transactions",
"Delete Transactions": "Delete Transactions",
"Unable to delete these transactions": "Unable to delete these transactions",
"Account List": "Danh sách tài khoản",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "更新收入交易的分类",
"Update Categories for Transfer Transactions": "更新转账交易的分类",
"Unable to update categories for transactions": "无法更新交易的分类",
"Update Accounts for Transactions": "更新交易的账户",
"Update Destination Accounts for Transactions": "更新交易的目标账户",
"Unable to update accounts for transactions": "无法更新交易的账户",
"Delete Transactions": "删除交易",
"Unable to delete these transactions": "无法删除这些交易",
"Account List": "账户列表",
+3
View File
@@ -1853,6 +1853,9 @@
"Update Categories for Income Transactions": "更新收入交易的分類",
"Update Categories for Transfer Transactions": "更新轉帳交易的分類",
"Unable to update categories for transactions": "無法更新交易的分類",
"Update Accounts for Transactions": "更新交易的帳戶",
"Update Destination Accounts for Transactions": "更新交易的目標帳戶",
"Unable to update accounts for transactions": "無法更新交易的帳戶",
"Delete Transactions": "刪除交易",
"Unable to delete these transactions": "無法刪除這些交易",
"Account List": "帳戶清單",
+6
View File
@@ -563,6 +563,12 @@ export interface TransactionBatchUpdateCategoryRequest {
readonly categoryId: string;
}
export interface TransactionBatchUpdateAccountRequest {
readonly transactionIds: string[];
readonly accountId: string;
readonly isDestinationAccount: boolean;
}
export interface TransactionMoveBetweenAccountsRequest {
readonly fromAccountId: string;
readonly toAccountId: string;
+35
View File
@@ -1164,6 +1164,40 @@ export const useTransactionsStore = defineStore('transactions', () => {
});
}
function batchUpdateTransactionAccounts({ transactionIds, accountId, isDestinationAccount }: { transactionIds: string[], accountId: string, isDestinationAccount: boolean }): Promise<boolean> {
return new Promise((resolve, reject) => {
services.batchUpdateTransactionAccounts({ transactionIds, accountId, isDestinationAccount }).then(response => {
const data = response.data;
if (!data || !data.success || !data.result) {
reject({ message: 'Unable to update accounts for transactions' });
return;
}
updateStoreInvalidState({
transactionList: true,
reconciliationStatement: true,
accountList: true,
overview: true,
statistics: true,
explorer: true
});
resolve(data.result);
}).catch(error => {
logger.error('failed to update accounts for transactions', error);
if (error.response && error.response.data && error.response.data.errorMessage) {
reject({ error: error.response.data });
} else if (!error.processed) {
reject({ message: 'Unable to update accounts for transactions' });
} else {
reject(error);
}
});
});
}
function moveAllTransactionsBetweenAccounts({ fromAccountId, toAccountId }: { fromAccountId: string, toAccountId: string }): Promise<boolean> {
return new Promise((resolve, reject) => {
services.moveAllTransactionsBetweenAccounts({ fromAccountId, toAccountId }).then(response => {
@@ -1539,6 +1573,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
getTransaction,
saveTransaction,
batchUpdateTransactionCategories,
batchUpdateTransactionAccounts,
moveAllTransactionsBetweenAccounts,
deleteTransaction,
batchDeleteTransactions,
@@ -0,0 +1,166 @@
<template>
<v-dialog width="600" :persistent="true" v-model="showState">
<v-card class="pa-sm-1 pa-md-2">
<template #title>
<div class="d-flex flex-wrap align-center">
<h4 class="text-h4 text-wrap" v-if="!isDestinationAccount">{{ tt('Update Accounts for Transactions') }}</h4>
<h4 class="text-h4 text-wrap" v-if="isDestinationAccount">{{ tt('Update Destination Accounts for Transactions') }}</h4>
<v-btn class="ms-2" density="compact" color="default" variant="text" size="24"
:icon="true" :disabled="loading || submitting" :loading="loading"
@click="reload">
<template #loader>
<v-progress-circular indeterminate size="20"/>
</template>
<v-icon :icon="mdiRefresh" size="24" />
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn>
</div>
</template>
<v-card-text class="w-100 d-flex justify-center">
<v-row>
<v-col cols="12">
<two-column-select primary-key-field="id" primary-value-field="category"
primary-title-field="name" primary-footer-field="displayBalance"
primary-icon-field="icon" primary-icon-type="account"
primary-sub-items-field="accounts"
:primary-title-i18n="true"
secondary-key-field="id" secondary-value-field="id"
secondary-title-field="name" secondary-footer-field="displayBalance"
secondary-icon-field="icon" secondary-icon-type="account" secondary-color-field="color"
:disabled="loading || !allVisibleAccounts.length"
:enable-filter="true" :filter-placeholder="tt('Find account')" :filter-no-items-text="tt('No available account')"
:custom-selection-primary-text="getAccountDisplayName(accountId)"
:label="!isDestinationAccount ? tt('Account') : tt('Destination Account')"
:placeholder="!isDestinationAccount ? tt('Account') : tt('Destination Account')"
:items="allVisibleCategorizedAccounts"
v-model="accountId">
</two-column-select>
</v-col>
</v-row>
</v-card-text>
<v-card-text>
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
<v-btn :disabled="loading || submitting || updateIds.length < 1 || !accountId" @click="confirm">
{{ tt('OK') }}
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn>
<v-btn color="secondary" variant="tonal" :disabled="loading || submitting" @click="cancel">{{ tt('Cancel') }}</v-btn>
</div>
</v-card-text>
</v-card>
</v-dialog>
<snack-bar ref="snackbar" />
</template>
<script setup lang="ts">
import SnackBar from '@/components/desktop/SnackBar.vue';
import { ref, computed, useTemplateRef } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { useSettingsStore } from '@/stores/setting.ts';
import { useAccountsStore } from '@/stores/account.ts';
import { useTransactionsStore } from '@/stores/transaction.ts';
import { Account, type CategorizedAccountWithDisplayBalance } from '@/models/account.ts';
import {
mdiRefresh
} from '@mdi/js';
type SnackBarType = InstanceType<typeof SnackBar>;
const {
tt,
getCategorizedAccountsWithDisplayBalance
} = useI18n();
const settingsStore = useSettingsStore();
const accountsStore = useAccountsStore();
const transactionsStore = useTransactionsStore();
const snackbar = useTemplateRef<SnackBarType>('snackbar');
const showState = ref<boolean>(false);
const loading = ref<boolean>(false);
const submitting = ref<boolean>(false);
const isDestinationAccount = ref<boolean>(false);
const updateIds = ref<string[]>([]);
const accountId = ref<string>('');
let resolveFunc: ((response: number) => void) | null = null;
let rejectFunc: ((reason?: unknown) => void) | null = null;
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
const customAccountCategoryOrder = computed<string>(() => settingsStore.appSettings.accountCategoryOrders);
const allAccounts = computed<Account[]>(() => accountsStore.allPlainAccounts);
const allVisibleAccounts = computed<Account[]>(() => accountsStore.allVisiblePlainAccounts);
const allVisibleCategorizedAccounts = computed<CategorizedAccountWithDisplayBalance[]>(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value, customAccountCategoryOrder.value));
function getAccountDisplayName(accountId?: string): string {
if (accountId) {
return Account.findAccountNameById(allAccounts.value, accountId) || '';
} else {
return tt('None');
}
}
function open(options: { isDestinationAccount: boolean; updateIds: string[] }): Promise<number> {
isDestinationAccount.value = options.isDestinationAccount;
updateIds.value = options.updateIds;
accountId.value = '';
submitting.value = false;
showState.value = true;
return new Promise((resolve, reject) => {
resolveFunc = resolve;
rejectFunc = reject;
});
}
function reload(): void {
loading.value = true;
accountsStore.loadAllAccounts({ force: true }).then(() => {
loading.value = false;
}).catch(error => {
loading.value = false;
if (!error.processed) {
snackbar.value?.showError(error);
}
});
}
function confirm(): void {
submitting.value = true;
transactionsStore.batchUpdateTransactionAccounts({
transactionIds: updateIds.value,
accountId: accountId.value,
isDestinationAccount: isDestinationAccount.value
}).then(() => {
submitting.value = false;
showState.value = false;
resolveFunc?.(updateIds.value.length);
}).catch(error => {
submitting.value = false;
if (!error.processed) {
snackbar.value?.showError(error);
}
});
}
function cancel(): void {
rejectFunc?.();
showState.value = false;
}
defineExpose({
open
});
</script>
@@ -91,6 +91,15 @@
:disabled="!isAllSelectedTransactionsTransfer"
@click="batchUpdateTransactionCategories(CategoryType.Transfer)"></v-list-item>
<v-divider class="my-2" />
<v-list-item :prepend-icon="mdiTextBoxEditOutline"
:title="tt('Update Accounts for Transactions')"
:disabled="selectedTransactionCount < 1"
@click="batchUpdateTransactionAccounts(false)"></v-list-item>
<v-list-item :prepend-icon="mdiTextBoxEditOutline"
:title="tt('Update Destination Accounts for Transactions')"
:disabled="!isAllSelectedTransactionsTransfer"
@click="batchUpdateTransactionAccounts(true)"></v-list-item>
<v-divider class="my-2" />
<v-list-item :prepend-icon="mdiDeleteOutline"
:title="tt('Delete Transactions')"
:disabled="selectedTransactionCount < 1"
@@ -180,6 +189,7 @@
</v-data-table>
<batch-update-category-dialog ref="batchUpdateCategoryDialog" />
<batch-update-account-dialog ref="batchUpdateAccountDialog" />
<batch-delete-dialog ref="batchDeleteDialog" />
<snack-bar ref="snackbar" />
</template>
@@ -188,6 +198,7 @@
import SnackBar from '@/components/desktop/SnackBar.vue';
import PaginationButtons from '@/components/desktop/PaginationButtons.vue';
import BatchUpdateCategoryDialog from '@/views/desktop/insights/dialogs/BatchUpdateCategoryDialog.vue';
import BatchUpdateAccountDialog from '@/views/desktop/insights/dialogs/BatchUpdateAccountDialog.vue';
import BatchDeleteDialog from '@/views/desktop/insights/dialogs/BatchDeleteDialog.vue';
import { ref, computed, useTemplateRef, watch } from 'vue';
@@ -216,6 +227,7 @@ import {
type SnackBarType = InstanceType<typeof SnackBar>;
type BatchUpdateCategoryDialogType = InstanceType<typeof BatchUpdateCategoryDialog>;
type BatchUpdateAccountDialogType = InstanceType<typeof BatchUpdateAccountDialog>;
type BatchDeleteDialogType = InstanceType<typeof BatchDeleteDialog>;
interface InsightsExplorerDataTableTabProps {
@@ -232,6 +244,7 @@ const emit = defineEmits<{
const snackbar = useTemplateRef<SnackBarType>('snackbar');
const batchUpdateCategoryDialog = useTemplateRef<BatchUpdateCategoryDialogType>('batchUpdateCategoryDialog');
const batchUpdateAccountDialog = useTemplateRef<BatchUpdateAccountDialogType>('batchUpdateAccountDialog');
const batchDeleteDialog = useTemplateRef<BatchDeleteDialogType>('batchDeleteDialog');
const {
@@ -333,6 +346,25 @@ function batchUpdateTransactionCategories(type: CategoryType): void {
});
}
function batchUpdateTransactionAccounts(isDestinationAccount: boolean): void {
batchUpdateAccountDialog.value?.open({
isDestinationAccount: isDestinationAccount,
updateIds: getAllSelectedTransactionIds()
}).then(updatedCount => {
if (updatedCount > 0) {
snackbar.value?.showMessage('format.misc.youHaveUpdatedTransactions', {
count: formatNumberToLocalizedNumerals(updatedCount)
});
}
selectedTransactions.value = {};
emit('update:transactions');
}).catch(error => {
if (error) {
snackbar.value?.showError(error);
}
});
}
function batchDeleteTransactions(): void {
batchDeleteDialog.value?.open({
updateIds: getAllSelectedTransactionIds()