support exporting data when checking pending import data

This commit is contained in:
MaysWind
2026-01-22 23:58:04 +08:00
parent 5d801a2343
commit 3bb7f5abf4
20 changed files with 163 additions and 23 deletions
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregoriano",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Grégorien",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "ಗ್ರೆಗೋರಿಯನ್",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorijanski",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "கிரிகோரியன்",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "เกรกอเรียน",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_mutabakat_ekstreleri",
"defaultImportDataMappingFileName": "ezBookkeeping_ice_aktarim_eslemesi",
"defaultImportHandlingScript": "ezBookkeeping_isleme_betigi",
"defaultImportReplaceRuleFileName": "ezBookkeeping_ice_aktarim_degistirme_kurali"
"defaultImportReplaceRuleFileName": "ezBookkeeping_ice_aktarim_degistirme_kurali",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Miladi",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_reconciliation_statements",
"defaultImportDataMappingFileName": "ezBookkeeping_import_data_mapping",
"defaultImportHandlingScript": "ezBookkeeping_handling_script",
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule"
"defaultImportReplaceRuleFileName": "ezBookkeeping_import_replace_rule",
"defaultImportCheckResultFileName": "ezBookkeeping_import_check_result"
},
"calendar": {
"Gregorian": "Gregorian",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_对账单",
"defaultImportDataMappingFileName": "ezBookkeeping_导入数据映射文件",
"defaultImportHandlingScript": "ezBookkeeping_导入处理脚本",
"defaultImportReplaceRuleFileName": "ezBookkeeping_导入替换规则文件"
"defaultImportReplaceRuleFileName": "ezBookkeeping_导入替换规则文件",
"defaultImportCheckResultFileName": "ezBookkeeping_导入检查结果"
},
"calendar": {
"Gregorian": "公历",
+2 -1
View File
@@ -149,7 +149,8 @@
"exportReconciliationStatementsFileName": "ezBookkeeping_{nickname}_對帳單",
"defaultImportDataMappingFileName": "ezBookkeeping_匯入資料對應檔案",
"defaultImportHandlingScript": "ezBookkeeping_匯入處理腳本",
"defaultImportReplaceRuleFileName": "ezBookkeeping_匯入替換規則檔案"
"defaultImportReplaceRuleFileName": "ezBookkeeping_匯入替換規則檔案",
"defaultImportCheckResultFileName": "ezBookkeeping_匯入檢查結果"
},
"calendar": {
"Gregorian": "公曆",
@@ -419,6 +419,8 @@ import { type NameValue, type NameNumeralValue, itemAndIndex, reversed, keys } f
import { type NumeralSystem, AmountFilterType } from '@/core/numeral.ts';
import { CategoryType } from '@/core/category.ts';
import { TransactionType } from '@/core/transaction.ts';
import { KnownFileType } from '@/core/file.ts';
import { ImportTransactionColumnType } from '@/core/import_transaction.ts';
import { Account, type CategorizedAccountWithDisplayBalance } from '@/models/account.ts';
import type { TransactionCategory } from '@/models/transaction_category.ts';
@@ -428,6 +430,7 @@ import { ImportTransaction } from '@/models/imported_transaction.ts';
import {
isString,
isNumber,
replaceAll,
objectFieldToArrayItem
} from '@/lib/common.ts';
import {
@@ -437,15 +440,14 @@ import {
parseDateTimeFromUnixTimeWithTimezoneOffset
} from '@/lib/datetime.ts';
import { formatCoordinate } from '@/lib/coordinate.ts';
import {
getAccountMapByName
} from '@/lib/account.ts';
import { getAccountMapByName } from '@/lib/account.ts';
import {
transactionTypeToCategoryType,
getSecondaryTransactionMapByName,
getTransactionPrimaryCategoryName,
getTransactionSecondaryCategoryName
} from '@/lib/category.ts';
import { startDownloadFile } from '@/lib/ui/common.ts';
import {
mdiCheck,
@@ -461,7 +463,9 @@ import {
mdiShapePlusOutline,
mdiPencilBoxMultipleOutline,
mdiNumericPositive1,
mdiNumericNegative1
mdiNumericNegative1,
mdiComma,
mdiKeyboardTab
} from '@mdi/js';
type SnackBarType = InstanceType<typeof SnackBar>;
@@ -504,6 +508,8 @@ const {
tt,
getCurrentNumeralSystemType,
formatDateTimeToLongDateTime,
formatDateTimeToGregorianDefaultDateTime,
formatAmountToWesternArabicNumeralsWithoutDigitGrouping,
formatAmountToLocalizedNumeralsWithCurrency,
getCategorizedAccountsWithDisplayBalance
} = useI18n();
@@ -937,6 +943,19 @@ const toolMenus = computed<ImportTransactionCheckDataMenu[]>(() => [
title: tt('Batch Convert Selected Amounts to Negative Values'),
disabled: isEditing.value || selectedImportTransactionCount.value < 1,
onClick: () => convertTransactionAmountSign(-1)
},
{
prependIcon: mdiComma,
title: tt('Export to CSV (Comma-separated values) File'),
disabled: isEditing.value || selectedImportTransactionCount.value < 1,
divider: true,
onClick: () => exportData(KnownFileType.CSV)
},
{
prependIcon: mdiKeyboardTab,
title: tt('Export to TSV (Tab-separated values) File'),
disabled: isEditing.value || selectedImportTransactionCount.value < 1,
onClick: () => exportData(KnownFileType.TSV)
}
]);
@@ -1320,6 +1339,20 @@ function getDisplayTimezone(transaction: ImportTransaction): string {
return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)}`;
}
function getDisplayTransactionType(transaction: ImportTransaction): string {
if (transaction.type === TransactionType.ModifyBalance) {
return tt('Modify Balance');
} else if (transaction.type === TransactionType.Income) {
return tt('Income');
} else if (transaction.type === TransactionType.Expense) {
return tt('Expense');
} else if (transaction.type === TransactionType.Transfer) {
return tt('Transfer');
} else {
return tt('Unknown');
}
}
function getDisplayCurrency(value: number, currencyCode: string): string {
return formatAmountToLocalizedNumeralsWithCurrency(value, currencyCode);
}
@@ -2079,6 +2112,94 @@ function changeCustomDateFilter(minTime: number, maxTime: number): void {
showCustomDateRangeDialog.value = false;
}
function exportData(fileType: KnownFileType): void {
if (!props.importTransactions || props.importTransactions.length < 1 || selectedImportTransactionCount.value < 1) {
return;
}
let separator = ',';
if (fileType === KnownFileType.TSV) {
separator = '\t';
}
const header = [
tt(ImportTransactionColumnType.TransactionTime.name),
tt(ImportTransactionColumnType.TransactionTimezone.name),
tt(ImportTransactionColumnType.TransactionType.name),
tt(ImportTransactionColumnType.Category.name),
tt(ImportTransactionColumnType.AccountName.name),
tt(ImportTransactionColumnType.AccountCurrency.name),
tt(ImportTransactionColumnType.Amount.name),
tt(ImportTransactionColumnType.RelatedAccountName.name),
tt(ImportTransactionColumnType.RelatedAccountCurrency.name),
tt(ImportTransactionColumnType.RelatedAmount.name),
tt(ImportTransactionColumnType.GeographicLocation.name),
tt(ImportTransactionColumnType.Tags.name),
tt(ImportTransactionColumnType.Description.name)
].join(separator) + '\n';
const transactions = props.importTransactions ?? [];
const rows = transactions.filter(transaction => transaction.selected).map(transaction => {
const transactionTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
const type = getDisplayTransactionType(transaction);
const accountName = transaction.sourceAccountId && transaction.sourceAccountId !== '0' && allAccountsMap.value[transaction.sourceAccountId] ? (allAccountsMap.value[transaction.sourceAccountId]?.name ?? transaction.originalSourceAccountName) : transaction.originalSourceAccountName;
const amountCurrency = transaction.sourceAccountId && transaction.sourceAccountId !== '0' && allAccountsMap.value[transaction.sourceAccountId] ? (allAccountsMap.value[transaction.sourceAccountId]?.currency ?? transaction.originalSourceAccountCurrency) : transaction.originalSourceAccountCurrency;
const amount = formatAmountToWesternArabicNumeralsWithoutDigitGrouping(transaction.sourceAmount);
const geographicLocation = transaction.geoLocation ? `${transaction.geoLocation.longitude} ${transaction.geoLocation.latitude}` : '';
let categoryName = transaction.categoryId && transaction.categoryId !== '0' && allCategoriesMap.value[transaction.categoryId] ? (allCategoriesMap.value[transaction.categoryId]?.name ?? transaction.originalCategoryName) : transaction.originalCategoryName;
let relatedAccountName: string | undefined = undefined;
let relatedAccountCurrency: string | undefined = undefined;
let relatedAmount: string | undefined = undefined;
if (transaction.type === TransactionType.ModifyBalance) {
categoryName = '';
} else if (transaction.type === TransactionType.Transfer) {
relatedAccountName = transaction.destinationAccountId && transaction.destinationAccountId !== '0' && allAccountsMap.value[transaction.destinationAccountId] ? (allAccountsMap.value[transaction.destinationAccountId]?.name ?? transaction.originalDestinationAccountName) : transaction.originalDestinationAccountName;
relatedAccountCurrency = transaction.destinationAccountId && transaction.destinationAccountId !== '0' && allAccountsMap.value[transaction.destinationAccountId] ? (allAccountsMap.value[transaction.destinationAccountId]?.currency ?? transaction.originalDestinationAccountCurrency) : transaction.originalDestinationAccountCurrency;
relatedAmount = formatAmountToWesternArabicNumeralsWithoutDigitGrouping(transaction.destinationAmount);
}
const tagNames: string[] = [];
if (transaction.tagIds && transaction.tagIds.length > 0) {
for (const [tagId, index] of itemAndIndex(transaction.tagIds)) {
let tagName = '';
if (tagId && tagId !== '0' && allTagsMap.value[tagId]) {
tagName = allTagsMap.value[tagId]!.name;
} else if (transaction.originalTagNames && transaction.originalTagNames[index]) {
tagName = transaction.originalTagNames[index] as string;
}
if (tagName) {
tagName = replaceAll(tagName, separator, ' ');
tagName = replaceAll(tagName, ';', ' ');
tagNames.push(tagName);
}
}
}
return [
formatDateTimeToGregorianDefaultDateTime(transactionTime),
getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset),
type,
replaceAll(categoryName, separator, ' '),
replaceAll(accountName, separator, ' '),
amountCurrency,
amount,
replaceAll(relatedAccountName ?? '', separator, ' '),
relatedAccountCurrency ?? '',
relatedAmount ?? '',
geographicLocation,
tagNames.join(';'),
replaceAll(transaction.comment || '', separator, ' ')
].join(separator);
});
startDownloadFile(fileType.formatFileName(tt('dataExport.defaultImportCheckResultFileName')), fileType.createBlob(header + rows.join('\n')));
}
function onShowDateRangeError(message: string): void {
snackbar.value?.showError(message);
}