batch create nonexistent transaction categories when import transaction
This commit is contained in:
@@ -164,6 +164,7 @@ export const ALL_ACCOUNT_ICONS: Record<string, IconInfo> = {
|
||||
export const DEFAULT_ACCOUNT_ICON = ALL_ACCOUNT_ICONS[DEFAULT_ACCOUNT_ICON_ID];
|
||||
|
||||
export const DEFAULT_CATEGORY_ICON_ID = '1';
|
||||
export const AUTOMATICALLY_CREATED_CATEGORY_ICON_ID = '561';
|
||||
|
||||
export const ALL_CATEGORY_ICONS: Record<string, IconInfo> = {
|
||||
// 1 - 99 : Expense - Food & Drink
|
||||
|
||||
@@ -1689,6 +1689,10 @@
|
||||
"Replace Invalid Transfer Categories": "Ungültige Überweisungskategorien ersetzen",
|
||||
"Replace Invalid Accounts": "Ungültige Konten ersetzen",
|
||||
"Replace Invalid Transaction Tags": "Ungültige Transaktions-Tags ersetzen",
|
||||
"Create Nonexistent Expense Categories": "Create Nonexistent Expense Categories",
|
||||
"Create Nonexistent Income Categories": "Create Nonexistent Income Categories",
|
||||
"Create Nonexistent Transfer Categories": "Create Nonexistent Transfer Categories",
|
||||
"Create Nonexistent Transaction Tags": "Create Nonexistent Transaction Tags",
|
||||
"Batch Convert Expense Transaction to Income Transaction": "Batch Convert Expense Transaction to Income Transaction",
|
||||
"Batch Convert Expense Transaction to Transfer Transaction": "Batch Convert Expense Transaction to Transfer Transaction",
|
||||
"Batch Convert Income Transaction to Expense Transaction": "Batch Convert Income Transaction to Expense Transaction",
|
||||
@@ -1697,6 +1701,9 @@
|
||||
"Batch Convert Transfer Transaction to Income Transaction": "Batch Convert Transfer Transaction to Income Transaction",
|
||||
"Invalid Category": "Ungültige Kategorie",
|
||||
"Target Category": "Zielkategorie",
|
||||
"Default Expense Category": "Default Expense Category",
|
||||
"Default Income Category": "Default Income Category",
|
||||
"Default Transfer Category": "Default Transfer Category",
|
||||
"Invalid Account": "Ungültiges Konto",
|
||||
"Target Account": "Zielkonto",
|
||||
"Invalid Tag": "Ungültiges Tag",
|
||||
|
||||
@@ -1689,6 +1689,10 @@
|
||||
"Replace Invalid Transfer Categories": "Replace Invalid Transfer Categories",
|
||||
"Replace Invalid Accounts": "Replace Invalid Accounts",
|
||||
"Replace Invalid Transaction Tags": "Replace Invalid Transaction Tags",
|
||||
"Create Nonexistent Expense Categories": "Create Nonexistent Expense Categories",
|
||||
"Create Nonexistent Income Categories": "Create Nonexistent Income Categories",
|
||||
"Create Nonexistent Transfer Categories": "Create Nonexistent Transfer Categories",
|
||||
"Create Nonexistent Transaction Tags": "Create Nonexistent Transaction Tags",
|
||||
"Batch Convert Expense Transaction to Income Transaction": "Batch Convert Expense Transaction to Income Transaction",
|
||||
"Batch Convert Expense Transaction to Transfer Transaction": "Batch Convert Expense Transaction to Transfer Transaction",
|
||||
"Batch Convert Income Transaction to Expense Transaction": "Batch Convert Income Transaction to Expense Transaction",
|
||||
@@ -1697,6 +1701,9 @@
|
||||
"Batch Convert Transfer Transaction to Income Transaction": "Batch Convert Transfer Transaction to Income Transaction",
|
||||
"Invalid Category": "Invalid Category",
|
||||
"Target Category": "Target Category",
|
||||
"Default Expense Category": "Default Expense Category",
|
||||
"Default Income Category": "Default Income Category",
|
||||
"Default Transfer Category": "Default Transfer Category",
|
||||
"Invalid Account": "Invalid Account",
|
||||
"Target Account": "Target Account",
|
||||
"Invalid Tag": "Invalid Tag",
|
||||
|
||||
@@ -1689,6 +1689,10 @@
|
||||
"Replace Invalid Transfer Categories": "Reemplazar categorías de transferencia no válidas",
|
||||
"Replace Invalid Accounts": "Reemplazar cuentas no válidas",
|
||||
"Replace Invalid Transaction Tags": "Reemplazar etiquetas de transacciones no válidas",
|
||||
"Create Nonexistent Expense Categories": "Create Nonexistent Expense Categories",
|
||||
"Create Nonexistent Income Categories": "Create Nonexistent Income Categories",
|
||||
"Create Nonexistent Transfer Categories": "Create Nonexistent Transfer Categories",
|
||||
"Create Nonexistent Transaction Tags": "Create Nonexistent Transaction Tags",
|
||||
"Batch Convert Expense Transaction to Income Transaction": "Batch Convert Expense Transaction to Income Transaction",
|
||||
"Batch Convert Expense Transaction to Transfer Transaction": "Batch Convert Expense Transaction to Transfer Transaction",
|
||||
"Batch Convert Income Transaction to Expense Transaction": "Batch Convert Income Transaction to Expense Transaction",
|
||||
@@ -1697,6 +1701,9 @@
|
||||
"Batch Convert Transfer Transaction to Income Transaction": "Batch Convert Transfer Transaction to Income Transaction",
|
||||
"Invalid Category": "Categoría no válida",
|
||||
"Target Category": "Categoría de destino",
|
||||
"Default Expense Category": "Default Expense Category",
|
||||
"Default Income Category": "Default Income Category",
|
||||
"Default Transfer Category": "Default Transfer Category",
|
||||
"Invalid Account": "Cuenta no válida",
|
||||
"Target Account": "Cuenta de destino",
|
||||
"Invalid Tag": "Etiqueta no válida",
|
||||
|
||||
@@ -1689,6 +1689,10 @@
|
||||
"Replace Invalid Transfer Categories": "無効な振替カテゴリを置き換えます",
|
||||
"Replace Invalid Accounts": "無効な口座を置き換えます",
|
||||
"Replace Invalid Transaction Tags": "無効な取引タグを置き換えます",
|
||||
"Create Nonexistent Expense Categories": "Create Nonexistent Expense Categories",
|
||||
"Create Nonexistent Income Categories": "Create Nonexistent Income Categories",
|
||||
"Create Nonexistent Transfer Categories": "Create Nonexistent Transfer Categories",
|
||||
"Create Nonexistent Transaction Tags": "Create Nonexistent Transaction Tags",
|
||||
"Batch Convert Expense Transaction to Income Transaction": "支出取引を収入取引に一括変換",
|
||||
"Batch Convert Expense Transaction to Transfer Transaction": "支出取引を振替取引に一括変換",
|
||||
"Batch Convert Income Transaction to Expense Transaction": "収入取引を支出取引に一括変換",
|
||||
@@ -1697,6 +1701,9 @@
|
||||
"Batch Convert Transfer Transaction to Income Transaction": "振替取引を収入取引に一括変換",
|
||||
"Invalid Category": "無効なカテゴリ",
|
||||
"Target Category": "対象カテゴリ",
|
||||
"Default Expense Category": "Default Expense Category",
|
||||
"Default Income Category": "Default Income Category",
|
||||
"Default Transfer Category": "Default Transfer Category",
|
||||
"Invalid Account": "無効な口座",
|
||||
"Target Account": "対象口座",
|
||||
"Invalid Tag": "無効なタグ",
|
||||
|
||||
@@ -1689,6 +1689,10 @@
|
||||
"Replace Invalid Transfer Categories": "Заменить недействительные категории переводов",
|
||||
"Replace Invalid Accounts": "Заменить недействительные счета",
|
||||
"Replace Invalid Transaction Tags": "Заменить недействительные теги транзакций",
|
||||
"Create Nonexistent Expense Categories": "Create Nonexistent Expense Categories",
|
||||
"Create Nonexistent Income Categories": "Create Nonexistent Income Categories",
|
||||
"Create Nonexistent Transfer Categories": "Create Nonexistent Transfer Categories",
|
||||
"Create Nonexistent Transaction Tags": "Create Nonexistent Transaction Tags",
|
||||
"Batch Convert Expense Transaction to Income Transaction": "Batch Convert Expense Transaction to Income Transaction",
|
||||
"Batch Convert Expense Transaction to Transfer Transaction": "Batch Convert Expense Transaction to Transfer Transaction",
|
||||
"Batch Convert Income Transaction to Expense Transaction": "Batch Convert Income Transaction to Expense Transaction",
|
||||
@@ -1697,6 +1701,9 @@
|
||||
"Batch Convert Transfer Transaction to Income Transaction": "Batch Convert Transfer Transaction to Income Transaction",
|
||||
"Invalid Category": "Недействительная категория",
|
||||
"Target Category": "Целевая категория",
|
||||
"Default Expense Category": "Default Expense Category",
|
||||
"Default Income Category": "Default Income Category",
|
||||
"Default Transfer Category": "Default Transfer Category",
|
||||
"Invalid Account": "Недействительный счет",
|
||||
"Target Account": "Целевой счет",
|
||||
"Invalid Tag": "Недействительный тег",
|
||||
|
||||
@@ -1689,6 +1689,10 @@
|
||||
"Replace Invalid Transfer Categories": "Thay thế các danh mục chuyển khoản không hợp lệ",
|
||||
"Replace Invalid Accounts": "Thay thế các tài khoản không hợp lệ",
|
||||
"Replace Invalid Transaction Tags": "Thay thế các thẻ giao dịch không hợp lệ",
|
||||
"Create Nonexistent Expense Categories": "Create Nonexistent Expense Categories",
|
||||
"Create Nonexistent Income Categories": "Create Nonexistent Income Categories",
|
||||
"Create Nonexistent Transfer Categories": "Create Nonexistent Transfer Categories",
|
||||
"Create Nonexistent Transaction Tags": "Create Nonexistent Transaction Tags",
|
||||
"Batch Convert Expense Transaction to Income Transaction": "Batch Convert Expense Transaction to Income Transaction",
|
||||
"Batch Convert Expense Transaction to Transfer Transaction": "Batch Convert Expense Transaction to Transfer Transaction",
|
||||
"Batch Convert Income Transaction to Expense Transaction": "Batch Convert Income Transaction to Expense Transaction",
|
||||
@@ -1697,6 +1701,9 @@
|
||||
"Batch Convert Transfer Transaction to Income Transaction": "Batch Convert Transfer Transaction to Income Transaction",
|
||||
"Invalid Category": "Danh mục không hợp lệ",
|
||||
"Target Category": "Danh mục mục tiêu",
|
||||
"Default Expense Category": "Default Expense Category",
|
||||
"Default Income Category": "Default Income Category",
|
||||
"Default Transfer Category": "Default Transfer Category",
|
||||
"Invalid Account": "Tài khoản không hợp lệ",
|
||||
"Target Account": "Tài khoản mục tiêu",
|
||||
"Invalid Tag": "Thẻ không hợp lệ",
|
||||
|
||||
@@ -1689,6 +1689,10 @@
|
||||
"Replace Invalid Transfer Categories": "替换无效的转账分类",
|
||||
"Replace Invalid Accounts": "替换无效的账户",
|
||||
"Replace Invalid Transaction Tags": "替换无效的交易标签",
|
||||
"Create Nonexistent Expense Categories": "创建不存在的支出分类",
|
||||
"Create Nonexistent Income Categories": "创建不存在的收入分类",
|
||||
"Create Nonexistent Transfer Categories": "创建不存在的转账分类",
|
||||
"Create Nonexistent Transaction Tags": "创建不存在的交易标签",
|
||||
"Batch Convert Expense Transaction to Income Transaction": "批量转换支出交易为收入交易",
|
||||
"Batch Convert Expense Transaction to Transfer Transaction": "批量转换支出交易为转账交易",
|
||||
"Batch Convert Income Transaction to Expense Transaction": "批量转换收入交易为支出交易",
|
||||
@@ -1697,6 +1701,9 @@
|
||||
"Batch Convert Transfer Transaction to Income Transaction": "批量转换转账交易为收入交易",
|
||||
"Invalid Category": "无效分类",
|
||||
"Target Category": "目标分类",
|
||||
"Default Expense Category": "默认支出分类",
|
||||
"Default Income Category": "默认收入分类",
|
||||
"Default Transfer Category": "默认转账分类",
|
||||
"Invalid Account": "无效账户",
|
||||
"Target Account": "目标账户",
|
||||
"Invalid Tag": "无效标签",
|
||||
|
||||
@@ -143,27 +143,44 @@
|
||||
:title="tt('Replace Invalid Transaction Tags')"
|
||||
@click="showReplaceInvalidItemDialog('tag', allInvalidTransactionTagNames)"></v-list-item>
|
||||
<v-divider class="my-2"/>
|
||||
<v-list-item :prepend-icon="mdiFindReplace"
|
||||
<v-list-item :prepend-icon="mdiShapePlusOutline"
|
||||
:disabled="!!editingTransaction || !allInvalidExpenseCategoryNames || allInvalidExpenseCategoryNames.length < 1"
|
||||
:title="tt('Create Nonexistent Expense Categories')"
|
||||
@click="showBatchCreateInvalidItemDialog('expenseCategory', allInvalidExpenseCategoryNames)"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiShapePlusOutline"
|
||||
:disabled="!!editingTransaction || !allInvalidIncomeCategoryNames || allInvalidIncomeCategoryNames.length < 1"
|
||||
:title="tt('Create Nonexistent Income Categories')"
|
||||
@click="showBatchCreateInvalidItemDialog('incomeCategory', allInvalidIncomeCategoryNames)"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiShapePlusOutline"
|
||||
:disabled="!!editingTransaction || !allInvalidTransferCategoryNames || allInvalidTransferCategoryNames.length < 1"
|
||||
:title="tt('Create Nonexistent Transfer Categories')"
|
||||
@click="showBatchCreateInvalidItemDialog('transferCategory', allInvalidTransferCategoryNames)"></v-list-item>
|
||||
<!-- <v-list-item :prepend-icon="mdiShapePlusOutline"-->
|
||||
<!-- :disabled="!!editingTransaction || !allInvalidTransactionTagNames || allInvalidTransactionTagNames.length < 1"-->
|
||||
<!-- :title="tt('Create Nonexistent Transaction Tags')"-->
|
||||
<!-- @click="showBatchCreateInvalidItemDialog('tag', allInvalidTransactionTagNames)"></v-list-item>-->
|
||||
<v-divider class="my-2"/>
|
||||
<v-list-item :prepend-icon="mdiTransfer"
|
||||
:disabled="!!editingTransaction || selectedExpenseTransactionCount < 1"
|
||||
:title="tt('Batch Convert Expense Transaction to Income Transaction')"
|
||||
@click="convertTransactionType(TransactionType.Expense, TransactionType.Income)"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiFindReplace"
|
||||
<v-list-item :prepend-icon="mdiTransfer"
|
||||
:disabled="!!editingTransaction || selectedExpenseTransactionCount < 1"
|
||||
:title="tt('Batch Convert Expense Transaction to Transfer Transaction')"
|
||||
@click="convertTransactionType(TransactionType.Expense, TransactionType.Transfer)"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiFindReplace"
|
||||
<v-list-item :prepend-icon="mdiTransfer"
|
||||
:disabled="!!editingTransaction || selectedIncomeTransactionCount < 1"
|
||||
:title="tt('Batch Convert Income Transaction to Expense Transaction')"
|
||||
@click="convertTransactionType(TransactionType.Income, TransactionType.Expense)"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiFindReplace"
|
||||
<v-list-item :prepend-icon="mdiTransfer"
|
||||
:disabled="!!editingTransaction || selectedIncomeTransactionCount < 1"
|
||||
:title="tt('Batch Convert Income Transaction to Transfer Transaction')"
|
||||
@click="convertTransactionType(TransactionType.Income, TransactionType.Transfer)"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiFindReplace"
|
||||
<v-list-item :prepend-icon="mdiTransfer"
|
||||
:disabled="!!editingTransaction || selectedTransferTransactionCount < 1"
|
||||
:title="tt('Batch Convert Transfer Transaction to Expense Transaction')"
|
||||
@click="convertTransactionType(TransactionType.Transfer, TransactionType.Expense)"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiFindReplace"
|
||||
<v-list-item :prepend-icon="mdiTransfer"
|
||||
:disabled="!!editingTransaction || selectedTransferTransactionCount < 1"
|
||||
:title="tt('Batch Convert Transfer Transaction to Income Transaction')"
|
||||
@click="convertTransactionType(TransactionType.Transfer, TransactionType.Income)"></v-list-item>
|
||||
@@ -812,6 +829,7 @@
|
||||
@dateRange:change="changeCustomDateFilter"
|
||||
@error="onShowDateRangeError" />
|
||||
<batch-replace-dialog ref="batchReplaceDialog" />
|
||||
<BatchCreateDialog ref="batchCreateDialog" />
|
||||
<confirm-dialog ref="confirmDialog"/>
|
||||
<snack-bar ref="snackbar" />
|
||||
<input ref="fileInput" type="file" style="display: none" :accept="supportedImportFileExtensions" @change="setImportFile($event)" />
|
||||
@@ -823,6 +841,7 @@ import PaginationButtons from '@/components/desktop/PaginationButtons.vue';
|
||||
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
||||
import SnackBar from '@/components/desktop/SnackBar.vue';
|
||||
import BatchReplaceDialog, { type BatchReplaceDialogDataType } from './dialogs/BatchReplaceDialog.vue';
|
||||
import BatchCreateDialog, { type BatchCreateDialogDataType } from './dialogs/BatchCreateDialog.vue';
|
||||
|
||||
import { ref, computed, useTemplateRef, watch } from 'vue';
|
||||
|
||||
@@ -885,6 +904,8 @@ import {
|
||||
mdiDotsVertical,
|
||||
mdiHelpCircleOutline,
|
||||
mdiFindReplace,
|
||||
mdiShapePlusOutline,
|
||||
mdiTransfer,
|
||||
mdiClose,
|
||||
mdiArrowRight,
|
||||
mdiSelectAll,
|
||||
@@ -898,6 +919,7 @@ import {
|
||||
type ConfirmDialogType = InstanceType<typeof ConfirmDialog>;
|
||||
type SnackBarType = InstanceType<typeof SnackBar>;
|
||||
type BatchReplaceDialogType = InstanceType<typeof BatchReplaceDialog>;
|
||||
type BatchCreateDialogType = InstanceType<typeof BatchCreateDialog>;
|
||||
|
||||
type ImportTransactionDialogStep = 'uploadFile' | 'defineColumn' | 'checkData' | 'finalResult';
|
||||
|
||||
@@ -941,6 +963,7 @@ const statisticsStore = useStatisticsStore();
|
||||
const confirmDialog = useTemplateRef<ConfirmDialogType>('confirmDialog');
|
||||
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
||||
const batchReplaceDialog = useTemplateRef<BatchReplaceDialogType>('batchReplaceDialog');
|
||||
const batchCreateDialog = useTemplateRef<BatchCreateDialogType>('batchCreateDialog');
|
||||
const fileInput = useTemplateRef<HTMLInputElement>('fileInput');
|
||||
|
||||
const showState = ref<boolean>(false);
|
||||
@@ -2507,6 +2530,78 @@ function showReplaceInvalidItemDialog(type: BatchReplaceDialogDataType, invalidI
|
||||
});
|
||||
}
|
||||
|
||||
function showBatchCreateInvalidItemDialog(type: BatchCreateDialogDataType, invalidItems: NameValue[]): void {
|
||||
if (editingTransaction.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
batchCreateDialog.value?.open({
|
||||
type: type,
|
||||
invalidItems: invalidItems
|
||||
}).then(result => {
|
||||
if (!result || !result.sourceTargetMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
let updatedCount = 0;
|
||||
|
||||
if (importTransactions.value) {
|
||||
const sourceTargetMap: Record<string, string> = result.sourceTargetMap;
|
||||
|
||||
for (let i = 0; i < importTransactions.value.length; i++) {
|
||||
const transaction: ImportTransaction = importTransactions.value[i];
|
||||
|
||||
if (transaction.valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let updated = false;
|
||||
|
||||
if (type === 'expenseCategory' || type === 'incomeCategory' || type === 'transferCategory') {
|
||||
const categoryId = transaction.categoryId;
|
||||
const originalCategoryName = transaction.originalCategoryName;
|
||||
const targetItem = sourceTargetMap[originalCategoryName];
|
||||
|
||||
if (transaction.type !== TransactionType.ModifyBalance && targetItem && (!categoryId || categoryId === '0' || !allCategoriesMap.value[categoryId])) {
|
||||
if (type === 'expenseCategory' && transaction.type === TransactionType.Expense) {
|
||||
transaction.categoryId = targetItem;
|
||||
updated = true;
|
||||
} else if (type === 'incomeCategory' && transaction.type === TransactionType.Income) {
|
||||
transaction.categoryId = targetItem;
|
||||
updated = true;
|
||||
} else if (type === 'transferCategory' && transaction.type === TransactionType.Transfer) {
|
||||
transaction.categoryId = targetItem;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
} else if (type === 'tag' && transaction.tagIds) {
|
||||
for (let j = 0; j < transaction.tagIds.length; j++) {
|
||||
const tagId = transaction.tagIds[j];
|
||||
const originalTagName = transaction.originalTagNames ? transaction.originalTagNames[j] : "";
|
||||
const targetItem = sourceTargetMap[originalTagName];
|
||||
|
||||
if (targetItem && (!tagId || tagId === '0' || !allTagsMap.value[tagId])) {
|
||||
transaction.tagIds[j] = targetItem;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
updatedCount++;
|
||||
updateTransactionData(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedCount > 0) {
|
||||
snackbar.value?.showMessage('format.misc.youHaveUpdatedTransactions', {
|
||||
count: updatedCount
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function convertTransactionType(fromType: TransactionType, toType: TransactionType): void {
|
||||
if (!importTransactions.value || importTransactions.value.length < 1) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<v-dialog width="600" :persistent="!!persistent" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<h4 class="text-h4" v-if="type === 'expenseCategory'">{{ tt('Create Nonexistent Expense Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="type === 'incomeCategory'">{{ tt('Create Nonexistent Income Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="type === 'transferCategory'">{{ tt('Create Nonexistent Transfer Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="type === 'tag'">{{ tt('Create Nonexistent Transaction Tags') }}</h4>
|
||||
</div>
|
||||
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||
:disabled="submitting || !invalidItems || !invalidItems.length" :icon="true">
|
||||
<v-icon :icon="mdiDotsVertical" />
|
||||
<v-menu activator="parent">
|
||||
<v-list>
|
||||
<v-list-item :prepend-icon="mdiSelectAll"
|
||||
:title="tt('Select All')"
|
||||
:disabled="!invalidItems || !invalidItems.length"
|
||||
@click="selectAllItems"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiSelect"
|
||||
:title="tt('Select None')"
|
||||
:disabled="!invalidItems || !invalidItems.length"
|
||||
@click="selectNoneItems"></v-list-item>
|
||||
<v-list-item :prepend-icon="mdiSelectInverse"
|
||||
:title="tt('Invert Selection')"
|
||||
:disabled="!invalidItems || !invalidItems.length"
|
||||
@click="selectInvertItems"></v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-list class="py-0" density="compact" select-strategy="classic"
|
||||
:disabled="submitting" v-model:selected="selectedNames">
|
||||
<v-list-item class="py-0"
|
||||
:key="item.value" :value="item.name" :title="item.name"
|
||||
v-for="item in invalidItems">
|
||||
<template #prepend="{ isActive }">
|
||||
<v-list-item-action start>
|
||||
<v-checkbox-btn :model-value="isActive"></v-checkbox-btn>
|
||||
</v-list-item-action>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-btn :disabled="submitting || !selectedNames || !selectedNames.length" @click="confirm">
|
||||
{{ tt('OK') }}
|
||||
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular>
|
||||
</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" :disabled="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, useTemplateRef } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import type { NameValue } from '@/core/base.ts';
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import { AUTOMATICALLY_CREATED_CATEGORY_ICON_ID } from '@/consts/icon.ts';
|
||||
import { DEFAULT_CATEGORY_COLOR } from '@/consts/color.ts';
|
||||
|
||||
import { type TransactionCategoryCreateRequest, type TransactionCategoryCreateWithSubCategories, TransactionCategory } from '@/models/transaction_category.ts';
|
||||
|
||||
import { isDefined, arrayItemToObjectField } from '@/lib/common.ts';
|
||||
|
||||
import {
|
||||
mdiSelectAll,
|
||||
mdiSelect,
|
||||
mdiSelectInverse,
|
||||
mdiDotsVertical
|
||||
} from '@mdi/js';
|
||||
|
||||
export type BatchCreateDialogDataType = 'expenseCategory' | 'incomeCategory' | 'transferCategory' | 'tag';
|
||||
|
||||
type SnackBarType = InstanceType<typeof SnackBar>;
|
||||
|
||||
interface BatchCreateDialogResponse {
|
||||
sourceTargetMap: Record<string, string>;
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
persistent?: boolean;
|
||||
}>();
|
||||
|
||||
const { tt } = useI18n();
|
||||
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
|
||||
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
||||
|
||||
const showState = ref<boolean>(false);
|
||||
const submitting = ref<boolean>(false);
|
||||
const type = ref<BatchCreateDialogDataType | ''>('');
|
||||
const invalidItems = ref<NameValue[] | undefined>([]);
|
||||
const selectedNames = ref<string[]>([]);
|
||||
|
||||
let resolveFunc: ((response: BatchCreateDialogResponse) => void) | null = null;
|
||||
let rejectFunc: ((reason?: unknown) => void) | null = null;
|
||||
|
||||
function buildBatchCreateCategoryResponse(createdCategories: Record<number, TransactionCategory[]>): BatchCreateDialogResponse {
|
||||
const displayNameSourceItemMap: Record<string, string> = {};
|
||||
const sourceTargetMap: Record<string, string> = {};
|
||||
|
||||
for (const item of (invalidItems.value || [])) {
|
||||
displayNameSourceItemMap[item.name] = item.value;
|
||||
}
|
||||
|
||||
for (const categoryType in createdCategories) {
|
||||
const categories = createdCategories[categoryType];
|
||||
|
||||
for (const category of categories) {
|
||||
if (!category.subCategories || category.subCategories.length < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const subCategory of category.subCategories) {
|
||||
const sourceItem = displayNameSourceItemMap[subCategory.name];
|
||||
|
||||
if (!isDefined(sourceItem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sourceTargetMap[sourceItem] = subCategory.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const response: BatchCreateDialogResponse = {
|
||||
sourceTargetMap: sourceTargetMap
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
function open(options: { type: BatchCreateDialogDataType, invalidItems?: NameValue[] }): Promise<BatchCreateDialogResponse> {
|
||||
type.value = options.type;
|
||||
invalidItems.value = options.invalidItems;
|
||||
selectedNames.value = [];
|
||||
|
||||
showState.value = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolveFunc = resolve;
|
||||
rejectFunc = reject;
|
||||
});
|
||||
}
|
||||
|
||||
function selectAllItems(): void {
|
||||
selectedNames.value = (invalidItems.value || []).map(item => item.name);
|
||||
}
|
||||
|
||||
function selectNoneItems(): void {
|
||||
selectedNames.value = [];
|
||||
}
|
||||
|
||||
function selectInvertItems(): void {
|
||||
const currentSelectedNames: Record<string, boolean> = arrayItemToObjectField(selectedNames.value, true);
|
||||
selectedNames.value = [];
|
||||
|
||||
for (const item of (invalidItems.value || [])) {
|
||||
if (!currentSelectedNames[item.name]) {
|
||||
selectedNames.value.push(item.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function confirm(): void {
|
||||
if (type.value === 'expenseCategory' || type.value === 'incomeCategory' || type.value === 'transferCategory') {
|
||||
submitting.value = true;
|
||||
|
||||
let categoryType: CategoryType = CategoryType.Expense;
|
||||
let primaryCategoryName = '';
|
||||
|
||||
if (type.value === 'expenseCategory') {
|
||||
categoryType = CategoryType.Expense;
|
||||
primaryCategoryName = tt('Default Expense Category');
|
||||
} else if (type.value === 'incomeCategory') {
|
||||
categoryType = CategoryType.Income;
|
||||
primaryCategoryName = tt('Default Income Category');
|
||||
} else if (type.value === 'transferCategory') {
|
||||
categoryType = CategoryType.Transfer;
|
||||
primaryCategoryName = tt('Default Transfer Category');
|
||||
}
|
||||
|
||||
const subCategories: TransactionCategoryCreateRequest[] = [];
|
||||
|
||||
for (const item of selectedNames.value) {
|
||||
const category: TransactionCategory = TransactionCategory.createNewCategory(categoryType);
|
||||
category.name = item;
|
||||
category.icon = AUTOMATICALLY_CREATED_CATEGORY_ICON_ID;
|
||||
subCategories.push(category.toCreateRequest(''));
|
||||
}
|
||||
|
||||
const submitCategories: TransactionCategoryCreateWithSubCategories[] = [{
|
||||
name: primaryCategoryName,
|
||||
type: categoryType,
|
||||
icon: AUTOMATICALLY_CREATED_CATEGORY_ICON_ID,
|
||||
color: DEFAULT_CATEGORY_COLOR,
|
||||
subCategories: subCategories
|
||||
}];
|
||||
|
||||
transactionCategoriesStore.addCategories({
|
||||
categories: submitCategories
|
||||
}).then(response => {
|
||||
transactionCategoriesStore.loadAllCategories({ force: false }).then(() => {
|
||||
submitting.value = false;
|
||||
showState.value = false;
|
||||
|
||||
resolveFunc?.(buildBatchCreateCategoryResponse(response));
|
||||
}).catch(error => {
|
||||
submitting.value = false;
|
||||
|
||||
if (!error.processed) {
|
||||
snackbar.value?.showError(error);
|
||||
}
|
||||
});
|
||||
}).catch(error => {
|
||||
submitting.value = false;
|
||||
|
||||
if (!error.processed) {
|
||||
snackbar.value?.showError(error);
|
||||
}
|
||||
});
|
||||
} else if (type.value === 'tag') {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function cancel(): void {
|
||||
rejectFunc?.();
|
||||
showState.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user