show process when importing a lot of transactions
This commit is contained in:
@@ -319,6 +319,7 @@ func startWebServer(c *core.CliContext) error {
|
||||
apiV1Route.POST("/transactions/parse_dsv_file.json", bindApi(api.Transactions.TransactionParseImportDsvFileDataHandler))
|
||||
apiV1Route.POST("/transactions/parse_import.json", bindApi(api.Transactions.TransactionParseImportFileHandler))
|
||||
apiV1Route.POST("/transactions/import.json", bindApi(api.Transactions.TransactionImportHandler))
|
||||
apiV1Route.GET("/transactions/import/process.json", bindApi(api.Transactions.TransactionImportProcessHandler))
|
||||
}
|
||||
|
||||
// Transaction Pictures
|
||||
|
||||
+69
-6
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -1344,11 +1345,21 @@ func (a *TransactionsApi) TransactionImportHandler(c *core.WebContext) (any, *er
|
||||
found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS, uid, transactionImportReq.ClientSessionId)
|
||||
|
||||
if found {
|
||||
log.Infof(c, "[transactions.TransactionImportHandler] another \"%s\" transactions has been imported for user \"uid:%d\"", remark, uid)
|
||||
count, err := utils.StringToInt(remark)
|
||||
items := strings.Split(remark, ":")
|
||||
|
||||
if err == nil {
|
||||
return count, nil
|
||||
if len(items) >= 2 {
|
||||
if items[0] == "finished" {
|
||||
log.Infof(c, "[transactions.TransactionImportHandler] another \"%s\" transactions has been imported for user \"uid:%d\"", items[1], uid)
|
||||
count, err := utils.StringToInt(items[1])
|
||||
|
||||
if err == nil {
|
||||
return count, nil
|
||||
}
|
||||
} else if items[0] == "processing" {
|
||||
return nil, errs.ErrRepeatedRequest
|
||||
}
|
||||
} else {
|
||||
log.Warnf(c, "[transactions.TransactionImportHandler] another transaction import task may be executing, but remark \"%s\" is invalid", remark)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1422,7 +1433,9 @@ func (a *TransactionsApi) TransactionImportHandler(c *core.WebContext) (any, *er
|
||||
newTransactions[i] = transaction
|
||||
}
|
||||
|
||||
err = a.transactions.BatchCreateTransactions(c, user.Uid, newTransactions, newTransactionTagIdsMap)
|
||||
err = a.transactions.BatchCreateTransactions(c, user.Uid, newTransactions, newTransactionTagIdsMap, func(currentProcess float64) {
|
||||
a.SetSubmissionRemarkIfEnable(duplicatechecker.DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS, uid, transactionImportReq.ClientSessionId, fmt.Sprintf("processing:%.2f", currentProcess))
|
||||
})
|
||||
count := len(newTransactions)
|
||||
|
||||
if err != nil {
|
||||
@@ -1432,11 +1445,61 @@ func (a *TransactionsApi) TransactionImportHandler(c *core.WebContext) (any, *er
|
||||
|
||||
log.Infof(c, "[transactions.TransactionImportHandler] user \"uid:%d\" has imported %d transactions successfully", uid, count)
|
||||
|
||||
a.SetSubmissionRemarkIfEnable(duplicatechecker.DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS, uid, transactionImportReq.ClientSessionId, utils.IntToString(count))
|
||||
a.SetSubmissionRemarkIfEnable(duplicatechecker.DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS, uid, transactionImportReq.ClientSessionId, fmt.Sprintf("finished:%d", count))
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// TransactionImportProcessHandler returns the process of specified transaction import task by request parameters for current user
|
||||
func (a *TransactionsApi) TransactionImportProcessHandler(c *core.WebContext) (any, *errs.Error) {
|
||||
var transactionImportProcessReq models.TransactionImportProcessRequest
|
||||
err := c.ShouldBindQuery(&transactionImportProcessReq)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[transactions.TransactionImportProcessHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
|
||||
if !a.CurrentConfig().EnableDuplicateSubmissionsCheck {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS, uid, transactionImportProcessReq.ClientSessionId)
|
||||
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
items := strings.Split(remark, ":")
|
||||
|
||||
if len(items) < 2 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if items[0] == "finished" {
|
||||
return 100, nil
|
||||
} else if items[0] != "processing" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
process, err := utils.StringToFloat64(items[1])
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[transactions.TransactionImportProcessHandler] parse process failed, because %s", err.Error())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if process < 0 {
|
||||
return nil, nil
|
||||
} else if process >= 100 {
|
||||
process = 100
|
||||
}
|
||||
|
||||
return process, nil
|
||||
}
|
||||
|
||||
func (a *TransactionsApi) filterTransactions(c *core.WebContext, uid int64, transactions []*models.Transaction, accountMap map[int64]*models.Account) []*models.Transaction {
|
||||
finalTransactions := make([]*models.Transaction, 0, len(transactions))
|
||||
|
||||
|
||||
@@ -810,7 +810,7 @@ func (l *UserDataCli) ImportTransaction(c *core.CliContext, username string, fil
|
||||
return errs.ErrOperationFailed
|
||||
}
|
||||
|
||||
err = l.transactions.BatchCreateTransactions(c, user.Uid, newTransactions, newTransactionTagIdsMap)
|
||||
err = l.transactions.BatchCreateTransactions(c, user.Uid, newTransactions, newTransactionTagIdsMap, nil)
|
||||
|
||||
if err != nil {
|
||||
log.CliErrorf(c, "[user_data.ImportTransaction] failed to create transaction, because %s", err.Error())
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package core
|
||||
|
||||
// TaskProcessUpdateHandler represents the task process update handler
|
||||
type TaskProcessUpdateHandler func(currentProcess float64)
|
||||
@@ -26,6 +26,7 @@ var (
|
||||
ErrUploadedFileEmpty = NewNormalError(NormalSubcategoryGlobal, 16, http.StatusBadRequest, "uploaded file is empty")
|
||||
ErrExceedMaxUploadFileSize = NewNormalError(NormalSubcategoryGlobal, 17, http.StatusBadRequest, "uploaded file size exceeds the maximum allowed size")
|
||||
ErrFailureCountLimitReached = NewNormalError(NormalSubcategoryGlobal, 18, http.StatusBadRequest, "failure count exceeded maximum limit")
|
||||
ErrRepeatedRequest = NewNormalError(NormalSubcategoryGlobal, 19, http.StatusBadRequest, "repeated request")
|
||||
)
|
||||
|
||||
// GetParameterInvalidMessage returns specific error message for invalid parameter error
|
||||
|
||||
@@ -149,6 +149,11 @@ type TransactionImportRequest struct {
|
||||
ClientSessionId string `json:"clientSessionId"`
|
||||
}
|
||||
|
||||
// TransactionImportProcessRequest represents all parameters of transaction import process request
|
||||
type TransactionImportProcessRequest struct {
|
||||
ClientSessionId string `form:"client_session_id"`
|
||||
}
|
||||
|
||||
// TransactionCountRequest represents transaction count request
|
||||
type TransactionCountRequest struct {
|
||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||
|
||||
@@ -2,6 +2,7 @@ package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -260,8 +261,11 @@ func (s *TransactionService) CreateTransaction(c core.Context, transaction *mode
|
||||
}
|
||||
|
||||
// BatchCreateTransactions saves new transactions to database
|
||||
func (s *TransactionService) BatchCreateTransactions(c core.Context, uid int64, transactions []*models.Transaction, allTagIds map[int][]int64) error {
|
||||
func (s *TransactionService) BatchCreateTransactions(c core.Context, uid int64, transactions []*models.Transaction, allTagIds map[int][]int64, processHandler core.TaskProcessUpdateHandler) error {
|
||||
now := time.Now().Unix()
|
||||
currentProcess := float64(0)
|
||||
processUpdateStep := int(math.Max(100.0, float64(len(transactions)/100.0)))
|
||||
|
||||
needTransactionUuidCount := uint16(0)
|
||||
needTagIndexUuidCount := uint16(0)
|
||||
|
||||
@@ -366,6 +370,12 @@ func (s *TransactionService) BatchCreateTransactions(c core.Context, uid int64,
|
||||
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
||||
err := s.doCreateTransaction(c, userDataDb, sess, transaction, transactionTagIndexes, transactionTagIds, nil, nil)
|
||||
|
||||
currentProcess = float64(i) / float64(len(transactions)) * 100
|
||||
|
||||
if processHandler != nil && i%processUpdateStep == 0 {
|
||||
processHandler(currentProcess)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
transactionUnixTime := utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime)
|
||||
transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
||||
|
||||
@@ -483,6 +483,11 @@ export default {
|
||||
timeout: DEFAULT_IMPORT_API_TIMEOUT
|
||||
} as ApiRequestConfig);
|
||||
},
|
||||
getImportTransactionsProcess: (clientSessionId: string): ApiResponsePromise<number | null> => {
|
||||
return axios.get<ApiResponse<number | null>>('v1/transactions/import/process.json?client_session_id=' + clientSessionId, {
|
||||
ignoreError: true
|
||||
} as ApiRequestConfig);
|
||||
},
|
||||
uploadTransactionPicture: ({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): ApiResponsePromise<TransactionPictureInfoBasicResponse> => {
|
||||
return axios.postForm<ApiResponse<TransactionPictureInfoBasicResponse>>('v1/transaction/pictures/upload.json', {
|
||||
picture: pictureFile,
|
||||
|
||||
+4
-1
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "{count} von {totalCount} ausgewählt",
|
||||
"youHaveUpdatedTransactions": "Sie haben {count} Transaktionen aktualisiert",
|
||||
"confirmImportTransactions": "Sind Sie sicher, dass Sie {count} Transaktionen importieren möchten?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "Sie haben {count} Transaktionen erfolgreich importiert.",
|
||||
"accountActivationAndResendValidationEmailTip": "Ein Aktivierungslink wurde an Ihre E-Mail-Adresse gesendet: {email}. Wenn Sie die E-Mail nicht erhalten haben, geben Sie bitte das Passwort erneut ein und klicken Sie auf die Schaltfläche unten, um die Bestätigungs-E-Mail erneut zu senden.",
|
||||
"resendValidationEmailTip": "Wenn Sie die E-Mail nicht erhalten haben, geben Sie bitte das Passwort erneut ein und klicken Sie auf die Schaltfläche unten, um die Bestätigungs-E-Mail an: {email} erneut zu senden."
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "Keine Dateien hochgeladen",
|
||||
"uploaded file is empty": "Hochgeladene Datei ist leer",
|
||||
"uploaded file size exceeds the maximum allowed size": "Hochgeladene Datei überschreitet die maximal zulässige Größe",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||
"Cannot import invalid transactions": "Ungültige Transaktionen können nicht importiert werden",
|
||||
"Unable to parse import file": "Importdatei kann nicht geparst werden",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "Ausgewählte Ausgabenkategorien im Batch ersetzen",
|
||||
"Batch Replace Selected Income Categories": "Ausgewählte Einnahmenkategorien im Batch ersetzen",
|
||||
"Batch Replace Selected Transfer Categories": "Ausgewählte Überweisungskategorien im Batch ersetzen",
|
||||
|
||||
+4
-1
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "Selected {count} of {totalCount}",
|
||||
"youHaveUpdatedTransactions": "You have updated {count} transactions",
|
||||
"confirmImportTransactions": "Are you sure you want to import {count} transactions?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "You have imported {count} transactions successfully.",
|
||||
"accountActivationAndResendValidationEmailTip": "Account activation link has been sent to your email address: {email}, If you don't receive the mail, please fill password again and click the button below to resend the validation mail.",
|
||||
"resendValidationEmailTip": "If you don't receive the mail, please fill password again and click the button below to resend the validation mail to: {email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "No files uploaded",
|
||||
"uploaded file is empty": "Uploaded file is empty",
|
||||
"uploaded file size exceeds the maximum allowed size": "Uploaded file size exceeds the maximum allowed size",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||
"Cannot import invalid transactions": "Cannot import invalid transactions",
|
||||
"Unable to parse import file": "Unable to parse import file",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "Batch Replace Selected Expense Categories",
|
||||
"Batch Replace Selected Income Categories": "Batch Replace Selected Income Categories",
|
||||
"Batch Replace Selected Transfer Categories": "Batch Replace Selected Transfer Categories",
|
||||
|
||||
+4
-1
@@ -101,6 +101,7 @@
|
||||
"selectedCount": "Seleccionado {count} de {totalCount}",
|
||||
"youHaveUpdatedTransactions": "Has actualizado {count} transacciones",
|
||||
"confirmImportTransactions": "¿Está seguro de que desea importar {count} transacciones?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "Ha importado {count} transacciones correctamente.",
|
||||
"accountActivationAndResendValidationEmailTip": "El enlace de activación de la cuenta se envió a su dirección de correo electrónico: {email}. Si no recibe el correo, ingrese la contraseña nuevamente y haga clic en el botón a continuación para reenviar el correo de validación.",
|
||||
"resendValidationEmailTip": "Si no recibe el correo, complete nuevamente la contraseña y haga clic en el botón a continuación para reenviar el correo de validación a: {email}"
|
||||
@@ -1178,7 +1179,8 @@
|
||||
"no files uploaded": "No se subieron archivos",
|
||||
"uploaded file is empty": "El archivo subido está vacío",
|
||||
"uploaded file size exceeds the maximum allowed size": "El tamaño del archivo cargado excede el tamaño máximo permitido",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "IDENTIFICACIÓN",
|
||||
@@ -1688,6 +1690,7 @@
|
||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||
"Cannot import invalid transactions": "No se pueden importar transacciones no válidas",
|
||||
"Unable to parse import file": "No se puede analizar el archivo de importación",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "Reemplazar por lotes categorías de gastos seleccionadas",
|
||||
"Batch Replace Selected Income Categories": "Reemplazo por lotes de categorías de ingresos seleccionadas",
|
||||
"Batch Replace Selected Transfer Categories": "Reemplazar por lotes las categorías de transferencia seleccionadas",
|
||||
|
||||
+4
-1
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "{count} selezionati su {totalCount}",
|
||||
"youHaveUpdatedTransactions": "Hai aggiornato {count} transazioni",
|
||||
"confirmImportTransactions": "Sei sicuro di voler importare {count} transazioni?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "Hai importato {count} transazioni.",
|
||||
"accountActivationAndResendValidationEmailTip": "Abbiamo inviato un link per l'attivazione del tuo account all'indirizzo {email}. Se non hai ricevuto la mail, inserisci nuovamente la password e premi il bottone per ritentare l'invio.",
|
||||
"resendValidationEmailTip": "Se non hai ricevuto la mail, inserisci nuovamente la password e premi il bottone per ritentare l'invio all'indirizzo: {email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "Nessun file caricato",
|
||||
"uploaded file is empty": "Il file caricato è vuoto",
|
||||
"uploaded file size exceeds the maximum allowed size": "La dimensione del file caricato supera la dimensione massima consentita",
|
||||
"failure count exceeded maximum limit": "Il conteggio dei fallimenti ha superato il limite massimo, riprova più tardi"
|
||||
"failure count exceeded maximum limit": "Il conteggio dei fallimenti ha superato il limite massimo, riprova più tardi",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "Formato importo transazione non impostato",
|
||||
"Cannot import invalid transactions": "Impossibile importare transazioni non valide",
|
||||
"Unable to parse import file": "Impossibile analizzare il file di importazione",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "Sostituisci in blocco categorie di spesa selezionate",
|
||||
"Batch Replace Selected Income Categories": "Sostituisci in blocco categorie di entrata selezionate",
|
||||
"Batch Replace Selected Transfer Categories": "Sostituisci in blocco categorie di trasferimento selezionate",
|
||||
|
||||
+4
-1
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "{count} / {totalCount}を選択",
|
||||
"youHaveUpdatedTransactions": "{count}件の取引を更新しました",
|
||||
"confirmImportTransactions": "本当に{count}件の取引をインポートしますか?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "{count}件の取引を正常にインポートしました。",
|
||||
"accountActivationAndResendValidationEmailTip": "アカウントの有効化リンクがメールアドレスに送信されました:{email}、メールが届かない場合はパスワードをもう一度入力して下のボタンをクリックして認証メールを再送信してください。",
|
||||
"resendValidationEmailTip": "メールが届かない場合は、パスワードをもう一度入力の上、以下のボタンをクリックして検証メールを再送信してください: {email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "アップロードされたファイルはありません",
|
||||
"uploaded file is empty": "アップロードされたファイルは空です",
|
||||
"uploaded file size exceeds the maximum allowed size": "アップロードされたファイルが最大許容サイズを超えています",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||
"Cannot import invalid transactions": "無効な取引をインポートできません",
|
||||
"Unable to parse import file": "インポートファイルを解析できません",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "バッチは選択した支出カテゴリを置き換えます",
|
||||
"Batch Replace Selected Income Categories": "バッチは選択した収入カテゴリを置き換えます",
|
||||
"Batch Replace Selected Transfer Categories": "バッチは選択した振替カテゴリを置き換えます",
|
||||
|
||||
+4
-1
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "Выбрано {count} из {totalCount}",
|
||||
"youHaveUpdatedTransactions": "Вы обновили {count} транзакций",
|
||||
"confirmImportTransactions": "Вы уверены, что хотите импортировать {count} транзакций?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "Вы успешно импортировали {count} транзакций.",
|
||||
"accountActivationAndResendValidationEmailTip": "Ссылка для активации учетной записи была отправлена на ваш электронный адрес: {email}. Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно.",
|
||||
"resendValidationEmailTip": "Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно на: {email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "Файлы не загружены",
|
||||
"uploaded file is empty": "Загруженный файл пуст",
|
||||
"uploaded file size exceeds the maximum allowed size": "Размер загруженного файла превышает максимально допустимый размер",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||
"Cannot import invalid transactions": "Невозможно импортировать недействительные транзакции",
|
||||
"Unable to parse import file": "Не удалось обработать файл импорта",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "Пакетная замена выбранных категорий расходов",
|
||||
"Batch Replace Selected Income Categories": "Пакетная замена выбранных категорий доходов",
|
||||
"Batch Replace Selected Transfer Categories": "Пакетная замена выбранных категорий переводов",
|
||||
|
||||
+4
-1
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "Вибрано {count} з {totalCount}",
|
||||
"youHaveUpdatedTransactions": "Ви оновили {count} транзакцій",
|
||||
"confirmImportTransactions": "Ви впевнені, що хочете імпортувати {count} транзакцій?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "Ви успішно імпортували {count} транзакцій.",
|
||||
"accountActivationAndResendValidationEmailTip": "Посилання для активації облікового запису було надіслано на вашу електронну адресу: {email}. Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно.",
|
||||
"resendValidationEmailTip": "Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно на адресу: {email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "Файли не завантажено",
|
||||
"uploaded file is empty": "Завантажений файл порожній",
|
||||
"uploaded file size exceeds the maximum allowed size": "Розмір завантаженого файлу перевищує максимально допустимий",
|
||||
"failure count exceeded maximum limit": "Кількість невдали спроб перевищила допустимий ліміт, спробуйте пізніше"
|
||||
"failure count exceeded maximum limit": "Кількість невдали спроб перевищила допустимий ліміт, спробуйте пізніше",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "Не вказано формат суми транзакцій",
|
||||
"Cannot import invalid transactions": "Неможливо імпортувати недійсні транзакції",
|
||||
"Unable to parse import file": "Не вдалося обробити файл імпорту",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "Пакетна заміна вибраних категорій витрат",
|
||||
"Batch Replace Selected Income Categories": "Пакетна заміна вибраних категорій доходів",
|
||||
"Batch Replace Selected Transfer Categories": "Пакетна заміна вибраних категорій переказів",
|
||||
|
||||
+4
-1
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "Đã chọn {count} trên {totalCount}",
|
||||
"youHaveUpdatedTransactions": "Bạn đã cập nhật {count} giao dịch",
|
||||
"confirmImportTransactions": "Bạn có chắc chắn muốn nhập {count} giao dịch không?",
|
||||
"importingTransactions": "Importing ({process}%)",
|
||||
"importTransactionResult": "Bạn đã nhập thành công {count} giao dịch.",
|
||||
"accountActivationAndResendValidationEmailTip": "Liên kết kích hoạt tài khoản đã được gửi tới email của bạn: {email}. Nếu bạn không nhận được email, vui lòng nhập lại mật khẩu và nhấp nút bên dưới để gửi lại email xác nhận.",
|
||||
"resendValidationEmailTip": "Nếu bạn không nhận được email, vui lòng nhập lại mật khẩu và nhấp nút bên dưới để gửi lại email xác nhận tới: {email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "Không có tệp nào được tải lên",
|
||||
"uploaded file is empty": "Tệp đã tải lên trống",
|
||||
"uploaded file size exceeds the maximum allowed size": "Kích thước tệp đã tải lên vượt quá kích thước tối đa cho phép",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time",
|
||||
"repeated request": "Repeated Request"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||
"Cannot import invalid transactions": "Không thể nhập giao dịch không hợp lệ",
|
||||
"Unable to parse import file": "Không thể phân tích tệp nhập",
|
||||
"Unable to import transactions": "Unable to import transactions",
|
||||
"Batch Replace Selected Expense Categories": "Thay thế hàng loạt các danh mục chi phí đã chọn",
|
||||
"Batch Replace Selected Income Categories": "Thay thế hàng loạt các danh mục thu nhập đã chọn",
|
||||
"Batch Replace Selected Transfer Categories": "Thay thế hàng loạt các danh mục chuyển khoản đã chọn",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "已选择 {count} / {totalCount}",
|
||||
"youHaveUpdatedTransactions": "您已经更新 {count} 个交易",
|
||||
"confirmImportTransactions": "您确定要导入 {count} 个交易?",
|
||||
"importingTransactions": "正在导入 ({process}%)",
|
||||
"importTransactionResult": "您已经成功导入 {count} 个交易。",
|
||||
"accountActivationAndResendValidationEmailTip": "账号激活链接已经发送到您的邮箱地址:{email},如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件。",
|
||||
"resendValidationEmailTip": "如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件到:{email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "没有上传文件",
|
||||
"uploaded file is empty": "上传的文件为空",
|
||||
"uploaded file size exceeds the maximum allowed size": "上传的文件大小超出了允许的最大大小",
|
||||
"failure count exceeded maximum limit": "失败次数超出最大限制,请稍后重试"
|
||||
"failure count exceeded maximum limit": "失败次数超出最大限制,请稍后重试",
|
||||
"repeated request": "重复的请求"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "交易金额格式没有设置",
|
||||
"Cannot import invalid transactions": "不能导入无效的交易",
|
||||
"Unable to parse import file": "无法解析导入的文件",
|
||||
"Unable to import transactions": "无法导入交易",
|
||||
"Batch Replace Selected Expense Categories": "批量替换选中的支出分类",
|
||||
"Batch Replace Selected Income Categories": "批量替换选中的收入分类",
|
||||
"Batch Replace Selected Transfer Categories": "批量替换选中的转账分类",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"selectedCount": "已選擇 {count} / {totalCount}",
|
||||
"youHaveUpdatedTransactions": "您已經更新 {count} 個交易",
|
||||
"confirmImportTransactions": "您確定要匯入 {count} 個交易?",
|
||||
"importingTransactions": "正在匯入 ({process}%)",
|
||||
"importTransactionResult": "您已經成功匯入 {count} 個交易。",
|
||||
"accountActivationAndResendValidationEmailTip": "帳號啟用連結已經傳送到您的信箱地址:{email},如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件。",
|
||||
"resendValidationEmailTip": "如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件到:{email}"
|
||||
@@ -1179,7 +1180,8 @@
|
||||
"no files uploaded": "沒有上傳檔案",
|
||||
"uploaded file is empty": "上傳的檔案為空",
|
||||
"uploaded file size exceeds the maximum allowed size": "上傳的檔案大小超出了允許的最大大小",
|
||||
"failure count exceeded maximum limit": "失敗次數超出最大限制,請稍後重試"
|
||||
"failure count exceeded maximum limit": "失敗次數超出最大限制,請稍後重試",
|
||||
"repeated request": "重複的請求"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
@@ -1689,6 +1691,7 @@
|
||||
"Transaction amount format is not set": "交易金額格式沒有設定",
|
||||
"Cannot import invalid transactions": "無法匯入無效的交易",
|
||||
"Unable to parse import file": "無法解析匯入的檔案",
|
||||
"Unable to import transactions": "無法匯入交易",
|
||||
"Batch Replace Selected Expense Categories": "批次替換選中的支出分類",
|
||||
"Batch Replace Selected Income Categories": "批次替換選中的收入分類",
|
||||
"Batch Replace Selected Transfer Categories": "批次替換選中的轉帳分類",
|
||||
|
||||
@@ -1143,6 +1143,31 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function getImportTransactionsProcess({ clientSessionId }: { clientSessionId: string }): Promise<number | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getImportTransactionsProcess(clientSessionId).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to get transactions import process' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('Unable to get transactions import process', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to get transactions import process' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function uploadTransactionPicture({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): Promise<TransactionPictureInfoBasicResponse> {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.uploadTransactionPicture({ pictureFile, clientSessionId }).then(response => {
|
||||
@@ -1243,6 +1268,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
parseImportDsvFile,
|
||||
parseImportTransaction,
|
||||
importTransactions,
|
||||
getImportTransactionsProcess,
|
||||
uploadTransactionPicture,
|
||||
removeUnusedTransactionPicture,
|
||||
getTransactionPictureUrl,
|
||||
|
||||
@@ -786,7 +786,7 @@
|
||||
<v-btn color="teal" :disabled="submitting || !!editingTransaction || selectedImportTransactionCount < 1 || selectedInvalidTransactionCount > 0"
|
||||
:append-icon="!submitting ? mdiArrowRight : undefined" @click="submit"
|
||||
v-if="currentStep === 'checkData'">
|
||||
{{ tt('Import') }}
|
||||
{{ (submitting && importProcess > 0 ? tt('format.misc.importingTransactions', { process: importProcess.toFixed(2) }) : tt('Import')) }}
|
||||
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular>
|
||||
</v-btn>
|
||||
<v-btn color="secondary" variant="tonal"
|
||||
@@ -969,6 +969,7 @@ const fileInput = useTemplateRef<HTMLInputElement>('fileInput');
|
||||
const showState = ref<boolean>(false);
|
||||
const clientSessionId = ref<string>('');
|
||||
const currentStep = ref<ImportTransactionDialogStep>('uploadFile');
|
||||
const importProcess = ref<number>(0);
|
||||
const fileType = ref<string>('ezbookkeeping');
|
||||
const fileSubType = ref<string>('ezbookkeeping_csv');
|
||||
const fileEncoding = ref<string>('utf-8');
|
||||
@@ -1927,6 +1928,7 @@ function open(): Promise<void> {
|
||||
fileSubType.value = 'ezbookkeeping_csv';
|
||||
fileEncoding.value = 'utf-8';
|
||||
currentStep.value = 'uploadFile';
|
||||
importProcess.value = 0;
|
||||
importFile.value = null;
|
||||
importData.value = '';
|
||||
parsedFileData.value = undefined;
|
||||
@@ -2221,10 +2223,48 @@ function submit(): void {
|
||||
editingTags.value = [];
|
||||
submitting.value = true;
|
||||
|
||||
let showProcessTimer : number | undefined = undefined;
|
||||
|
||||
if (transactions.length > 100) {
|
||||
setTimeout(() => {
|
||||
if (!submitting.value) {
|
||||
logger.warn('transaction import is not submitting');
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error the return value of setInterval is number, but lint shows it as NodeJS.Timer
|
||||
showProcessTimer = setInterval(() => {
|
||||
if (submitting.value) {
|
||||
transactionsStore.getImportTransactionsProcess({
|
||||
clientSessionId: clientSessionId.value
|
||||
}).then(response => {
|
||||
if (isNumber(response) && 0 <= response && response < 100) {
|
||||
importProcess.value = response;
|
||||
} else {
|
||||
importProcess.value = 0;
|
||||
clearInterval(showProcessTimer);
|
||||
showProcessTimer = undefined;
|
||||
}
|
||||
}).catch(() => {
|
||||
importProcess.value = 0;
|
||||
clearInterval(showProcessTimer);
|
||||
showProcessTimer = undefined;
|
||||
});
|
||||
}
|
||||
}, 2000);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
transactionsStore.importTransactions({
|
||||
transactions: transactions,
|
||||
clientSessionId: clientSessionId.value
|
||||
}).then(response => {
|
||||
if (showProcessTimer) {
|
||||
importProcess.value = 0;
|
||||
clearInterval(showProcessTimer);
|
||||
showProcessTimer = undefined;
|
||||
}
|
||||
|
||||
importedCount.value = response;
|
||||
currentStep.value = 'finalResult';
|
||||
|
||||
@@ -2235,6 +2275,12 @@ function submit(): void {
|
||||
|
||||
submitting.value = false;
|
||||
}).catch(error => {
|
||||
if (showProcessTimer) {
|
||||
importProcess.value = 0;
|
||||
clearInterval(showProcessTimer);
|
||||
showProcessTimer = undefined;
|
||||
}
|
||||
|
||||
submitting.value = false;
|
||||
|
||||
if (!error.processed) {
|
||||
|
||||
Reference in New Issue
Block a user