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_dsv_file.json", bindApi(api.Transactions.TransactionParseImportDsvFileDataHandler))
|
||||||
apiV1Route.POST("/transactions/parse_import.json", bindApi(api.Transactions.TransactionParseImportFileHandler))
|
apiV1Route.POST("/transactions/parse_import.json", bindApi(api.Transactions.TransactionParseImportFileHandler))
|
||||||
apiV1Route.POST("/transactions/import.json", bindApi(api.Transactions.TransactionImportHandler))
|
apiV1Route.POST("/transactions/import.json", bindApi(api.Transactions.TransactionImportHandler))
|
||||||
|
apiV1Route.GET("/transactions/import/process.json", bindApi(api.Transactions.TransactionImportProcessHandler))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction Pictures
|
// Transaction Pictures
|
||||||
|
|||||||
+69
-6
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"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)
|
found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS, uid, transactionImportReq.ClientSessionId)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
log.Infof(c, "[transactions.TransactionImportHandler] another \"%s\" transactions has been imported for user \"uid:%d\"", remark, uid)
|
items := strings.Split(remark, ":")
|
||||||
count, err := utils.StringToInt(remark)
|
|
||||||
|
|
||||||
if err == nil {
|
if len(items) >= 2 {
|
||||||
return count, nil
|
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
|
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)
|
count := len(newTransactions)
|
||||||
|
|
||||||
if err != nil {
|
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)
|
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
|
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 {
|
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))
|
finalTransactions := make([]*models.Transaction, 0, len(transactions))
|
||||||
|
|
||||||
|
|||||||
@@ -810,7 +810,7 @@ func (l *UserDataCli) ImportTransaction(c *core.CliContext, username string, fil
|
|||||||
return errs.ErrOperationFailed
|
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 {
|
if err != nil {
|
||||||
log.CliErrorf(c, "[user_data.ImportTransaction] failed to create transaction, because %s", err.Error())
|
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")
|
ErrUploadedFileEmpty = NewNormalError(NormalSubcategoryGlobal, 16, http.StatusBadRequest, "uploaded file is empty")
|
||||||
ErrExceedMaxUploadFileSize = NewNormalError(NormalSubcategoryGlobal, 17, http.StatusBadRequest, "uploaded file size exceeds the maximum allowed size")
|
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")
|
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
|
// GetParameterInvalidMessage returns specific error message for invalid parameter error
|
||||||
|
|||||||
@@ -149,6 +149,11 @@ type TransactionImportRequest struct {
|
|||||||
ClientSessionId string `json:"clientSessionId"`
|
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
|
// TransactionCountRequest represents transaction count request
|
||||||
type TransactionCountRequest struct {
|
type TransactionCountRequest struct {
|
||||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -260,8 +261,11 @@ func (s *TransactionService) CreateTransaction(c core.Context, transaction *mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BatchCreateTransactions saves new transactions to database
|
// 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()
|
now := time.Now().Unix()
|
||||||
|
currentProcess := float64(0)
|
||||||
|
processUpdateStep := int(math.Max(100.0, float64(len(transactions)/100.0)))
|
||||||
|
|
||||||
needTransactionUuidCount := uint16(0)
|
needTransactionUuidCount := uint16(0)
|
||||||
needTagIndexUuidCount := uint16(0)
|
needTagIndexUuidCount := uint16(0)
|
||||||
|
|
||||||
@@ -366,6 +370,12 @@ func (s *TransactionService) BatchCreateTransactions(c core.Context, uid int64,
|
|||||||
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
||||||
err := s.doCreateTransaction(c, userDataDb, sess, transaction, transactionTagIndexes, transactionTagIds, nil, nil)
|
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 {
|
if err != nil {
|
||||||
transactionUnixTime := utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime)
|
transactionUnixTime := utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime)
|
||||||
transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
||||||
|
|||||||
@@ -483,6 +483,11 @@ export default {
|
|||||||
timeout: DEFAULT_IMPORT_API_TIMEOUT
|
timeout: DEFAULT_IMPORT_API_TIMEOUT
|
||||||
} as ApiRequestConfig);
|
} 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> => {
|
uploadTransactionPicture: ({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): ApiResponsePromise<TransactionPictureInfoBasicResponse> => {
|
||||||
return axios.postForm<ApiResponse<TransactionPictureInfoBasicResponse>>('v1/transaction/pictures/upload.json', {
|
return axios.postForm<ApiResponse<TransactionPictureInfoBasicResponse>>('v1/transaction/pictures/upload.json', {
|
||||||
picture: pictureFile,
|
picture: pictureFile,
|
||||||
|
|||||||
+4
-1
@@ -102,6 +102,7 @@
|
|||||||
"selectedCount": "{count} von {totalCount} ausgewählt",
|
"selectedCount": "{count} von {totalCount} ausgewählt",
|
||||||
"youHaveUpdatedTransactions": "Sie haben {count} Transaktionen aktualisiert",
|
"youHaveUpdatedTransactions": "Sie haben {count} Transaktionen aktualisiert",
|
||||||
"confirmImportTransactions": "Sind Sie sicher, dass Sie {count} Transaktionen importieren möchten?",
|
"confirmImportTransactions": "Sind Sie sicher, dass Sie {count} Transaktionen importieren möchten?",
|
||||||
|
"importingTransactions": "Importing ({process}%)",
|
||||||
"importTransactionResult": "Sie haben {count} Transaktionen erfolgreich importiert.",
|
"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.",
|
"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."
|
"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",
|
"no files uploaded": "Keine Dateien hochgeladen",
|
||||||
"uploaded file is empty": "Hochgeladene Datei ist leer",
|
"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",
|
"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": {
|
"parameter": {
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
@@ -1689,6 +1691,7 @@
|
|||||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||||
"Cannot import invalid transactions": "Ungültige Transaktionen können nicht importiert werden",
|
"Cannot import invalid transactions": "Ungültige Transaktionen können nicht importiert werden",
|
||||||
"Unable to parse import file": "Importdatei kann nicht geparst 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 Expense Categories": "Ausgewählte Ausgabenkategorien im Batch ersetzen",
|
||||||
"Batch Replace Selected Income Categories": "Ausgewählte Einnahmenkategorien 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",
|
"Batch Replace Selected Transfer Categories": "Ausgewählte Überweisungskategorien im Batch ersetzen",
|
||||||
|
|||||||
+4
-1
@@ -102,6 +102,7 @@
|
|||||||
"selectedCount": "Selected {count} of {totalCount}",
|
"selectedCount": "Selected {count} of {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "You have updated {count} transactions",
|
"youHaveUpdatedTransactions": "You have updated {count} transactions",
|
||||||
"confirmImportTransactions": "Are you sure you want to import {count} transactions?",
|
"confirmImportTransactions": "Are you sure you want to import {count} transactions?",
|
||||||
|
"importingTransactions": "Importing ({process}%)",
|
||||||
"importTransactionResult": "You have imported {count} transactions successfully.",
|
"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.",
|
"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}"
|
"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",
|
"no files uploaded": "No files uploaded",
|
||||||
"uploaded file is empty": "Uploaded file is empty",
|
"uploaded file is empty": "Uploaded file is empty",
|
||||||
"uploaded file size exceeds the maximum allowed size": "Uploaded file size exceeds the maximum allowed size",
|
"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": {
|
"parameter": {
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
@@ -1689,6 +1691,7 @@
|
|||||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||||
"Cannot import invalid transactions": "Cannot import invalid transactions",
|
"Cannot import invalid transactions": "Cannot import invalid transactions",
|
||||||
"Unable to parse import file": "Unable to parse import file",
|
"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 Expense Categories": "Batch Replace Selected Expense Categories",
|
||||||
"Batch Replace Selected Income Categories": "Batch Replace Selected Income Categories",
|
"Batch Replace Selected Income Categories": "Batch Replace Selected Income Categories",
|
||||||
"Batch Replace Selected Transfer Categories": "Batch Replace Selected Transfer Categories",
|
"Batch Replace Selected Transfer Categories": "Batch Replace Selected Transfer Categories",
|
||||||
|
|||||||
+4
-1
@@ -101,6 +101,7 @@
|
|||||||
"selectedCount": "Seleccionado {count} de {totalCount}",
|
"selectedCount": "Seleccionado {count} de {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "Has actualizado {count} transacciones",
|
"youHaveUpdatedTransactions": "Has actualizado {count} transacciones",
|
||||||
"confirmImportTransactions": "¿Está seguro de que desea importar {count} transacciones?",
|
"confirmImportTransactions": "¿Está seguro de que desea importar {count} transacciones?",
|
||||||
|
"importingTransactions": "Importing ({process}%)",
|
||||||
"importTransactionResult": "Ha importado {count} transacciones correctamente.",
|
"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.",
|
"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}"
|
"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",
|
"no files uploaded": "No se subieron archivos",
|
||||||
"uploaded file is empty": "El archivo subido está vacío",
|
"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",
|
"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": {
|
"parameter": {
|
||||||
"id": "IDENTIFICACIÓN",
|
"id": "IDENTIFICACIÓN",
|
||||||
@@ -1688,6 +1690,7 @@
|
|||||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
"Transaction amount format is not set": "Transaction amount format is not set",
|
||||||
"Cannot import invalid transactions": "No se pueden importar transacciones no válidas",
|
"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 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 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 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",
|
"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}",
|
"selectedCount": "{count} selezionati su {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "Hai aggiornato {count} transazioni",
|
"youHaveUpdatedTransactions": "Hai aggiornato {count} transazioni",
|
||||||
"confirmImportTransactions": "Sei sicuro di voler importare {count} transazioni?",
|
"confirmImportTransactions": "Sei sicuro di voler importare {count} transazioni?",
|
||||||
|
"importingTransactions": "Importing ({process}%)",
|
||||||
"importTransactionResult": "Hai importato {count} transazioni.",
|
"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.",
|
"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}"
|
"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",
|
"no files uploaded": "Nessun file caricato",
|
||||||
"uploaded file is empty": "Il file caricato è vuoto",
|
"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",
|
"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": {
|
"parameter": {
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
@@ -1689,6 +1691,7 @@
|
|||||||
"Transaction amount format is not set": "Formato importo transazione non impostato",
|
"Transaction amount format is not set": "Formato importo transazione non impostato",
|
||||||
"Cannot import invalid transactions": "Impossibile importare transazioni non valide",
|
"Cannot import invalid transactions": "Impossibile importare transazioni non valide",
|
||||||
"Unable to parse import file": "Impossibile analizzare il file di importazione",
|
"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 Expense Categories": "Sostituisci in blocco categorie di spesa selezionate",
|
||||||
"Batch Replace Selected Income Categories": "Sostituisci in blocco categorie di entrata 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",
|
"Batch Replace Selected Transfer Categories": "Sostituisci in blocco categorie di trasferimento selezionate",
|
||||||
|
|||||||
+4
-1
@@ -102,6 +102,7 @@
|
|||||||
"selectedCount": "{count} / {totalCount}を選択",
|
"selectedCount": "{count} / {totalCount}を選択",
|
||||||
"youHaveUpdatedTransactions": "{count}件の取引を更新しました",
|
"youHaveUpdatedTransactions": "{count}件の取引を更新しました",
|
||||||
"confirmImportTransactions": "本当に{count}件の取引をインポートしますか?",
|
"confirmImportTransactions": "本当に{count}件の取引をインポートしますか?",
|
||||||
|
"importingTransactions": "Importing ({process}%)",
|
||||||
"importTransactionResult": "{count}件の取引を正常にインポートしました。",
|
"importTransactionResult": "{count}件の取引を正常にインポートしました。",
|
||||||
"accountActivationAndResendValidationEmailTip": "アカウントの有効化リンクがメールアドレスに送信されました:{email}、メールが届かない場合はパスワードをもう一度入力して下のボタンをクリックして認証メールを再送信してください。",
|
"accountActivationAndResendValidationEmailTip": "アカウントの有効化リンクがメールアドレスに送信されました:{email}、メールが届かない場合はパスワードをもう一度入力して下のボタンをクリックして認証メールを再送信してください。",
|
||||||
"resendValidationEmailTip": "メールが届かない場合は、パスワードをもう一度入力の上、以下のボタンをクリックして検証メールを再送信してください: {email}"
|
"resendValidationEmailTip": "メールが届かない場合は、パスワードをもう一度入力の上、以下のボタンをクリックして検証メールを再送信してください: {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": {
|
"parameter": {
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
@@ -1689,6 +1691,7 @@
|
|||||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
"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
@@ -102,6 +102,7 @@
|
|||||||
"selectedCount": "Выбрано {count} из {totalCount}",
|
"selectedCount": "Выбрано {count} из {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "Вы обновили {count} транзакций",
|
"youHaveUpdatedTransactions": "Вы обновили {count} транзакций",
|
||||||
"confirmImportTransactions": "Вы уверены, что хотите импортировать {count} транзакций?",
|
"confirmImportTransactions": "Вы уверены, что хотите импортировать {count} транзакций?",
|
||||||
|
"importingTransactions": "Importing ({process}%)",
|
||||||
"importTransactionResult": "Вы успешно импортировали {count} транзакций.",
|
"importTransactionResult": "Вы успешно импортировали {count} транзакций.",
|
||||||
"accountActivationAndResendValidationEmailTip": "Ссылка для активации учетной записи была отправлена на ваш электронный адрес: {email}. Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно.",
|
"accountActivationAndResendValidationEmailTip": "Ссылка для активации учетной записи была отправлена на ваш электронный адрес: {email}. Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно.",
|
||||||
"resendValidationEmailTip": "Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно на: {email}"
|
"resendValidationEmailTip": "Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно на: {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": {
|
"parameter": {
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
@@ -1689,6 +1691,7 @@
|
|||||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
"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
@@ -102,6 +102,7 @@
|
|||||||
"selectedCount": "Вибрано {count} з {totalCount}",
|
"selectedCount": "Вибрано {count} з {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "Ви оновили {count} транзакцій",
|
"youHaveUpdatedTransactions": "Ви оновили {count} транзакцій",
|
||||||
"confirmImportTransactions": "Ви впевнені, що хочете імпортувати {count} транзакцій?",
|
"confirmImportTransactions": "Ви впевнені, що хочете імпортувати {count} транзакцій?",
|
||||||
|
"importingTransactions": "Importing ({process}%)",
|
||||||
"importTransactionResult": "Ви успішно імпортували {count} транзакцій.",
|
"importTransactionResult": "Ви успішно імпортували {count} транзакцій.",
|
||||||
"accountActivationAndResendValidationEmailTip": "Посилання для активації облікового запису було надіслано на вашу електронну адресу: {email}. Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно.",
|
"accountActivationAndResendValidationEmailTip": "Посилання для активації облікового запису було надіслано на вашу електронну адресу: {email}. Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно.",
|
||||||
"resendValidationEmailTip": "Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно на адресу: {email}"
|
"resendValidationEmailTip": "Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно на адресу: {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": "Кількість невдали спроб перевищила допустимий ліміт, спробуйте пізніше",
|
||||||
|
"repeated request": "Repeated Request"
|
||||||
},
|
},
|
||||||
"parameter": {
|
"parameter": {
|
||||||
"id": "ID",
|
"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
@@ -102,6 +102,7 @@
|
|||||||
"selectedCount": "Đã chọn {count} trên {totalCount}",
|
"selectedCount": "Đã chọn {count} trên {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "Bạn đã cập nhật {count} giao dịch",
|
"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?",
|
"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.",
|
"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.",
|
"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}"
|
"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",
|
"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 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",
|
"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": {
|
"parameter": {
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
@@ -1689,6 +1691,7 @@
|
|||||||
"Transaction amount format is not set": "Transaction amount format is not set",
|
"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ệ",
|
"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 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 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 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",
|
"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}",
|
"selectedCount": "已选择 {count} / {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "您已经更新 {count} 个交易",
|
"youHaveUpdatedTransactions": "您已经更新 {count} 个交易",
|
||||||
"confirmImportTransactions": "您确定要导入 {count} 个交易?",
|
"confirmImportTransactions": "您确定要导入 {count} 个交易?",
|
||||||
|
"importingTransactions": "正在导入 ({process}%)",
|
||||||
"importTransactionResult": "您已经成功导入 {count} 个交易。",
|
"importTransactionResult": "您已经成功导入 {count} 个交易。",
|
||||||
"accountActivationAndResendValidationEmailTip": "账号激活链接已经发送到您的邮箱地址:{email},如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件。",
|
"accountActivationAndResendValidationEmailTip": "账号激活链接已经发送到您的邮箱地址:{email},如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件。",
|
||||||
"resendValidationEmailTip": "如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件到:{email}"
|
"resendValidationEmailTip": "如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件到:{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": "失败次数超出最大限制,请稍后重试",
|
||||||
|
"repeated request": "重复的请求"
|
||||||
},
|
},
|
||||||
"parameter": {
|
"parameter": {
|
||||||
"id": "ID",
|
"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": "无法导入交易",
|
||||||
"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": "批量替换选中的转账分类",
|
||||||
|
|||||||
@@ -102,6 +102,7 @@
|
|||||||
"selectedCount": "已選擇 {count} / {totalCount}",
|
"selectedCount": "已選擇 {count} / {totalCount}",
|
||||||
"youHaveUpdatedTransactions": "您已經更新 {count} 個交易",
|
"youHaveUpdatedTransactions": "您已經更新 {count} 個交易",
|
||||||
"confirmImportTransactions": "您確定要匯入 {count} 個交易?",
|
"confirmImportTransactions": "您確定要匯入 {count} 個交易?",
|
||||||
|
"importingTransactions": "正在匯入 ({process}%)",
|
||||||
"importTransactionResult": "您已經成功匯入 {count} 個交易。",
|
"importTransactionResult": "您已經成功匯入 {count} 個交易。",
|
||||||
"accountActivationAndResendValidationEmailTip": "帳號啟用連結已經傳送到您的信箱地址:{email},如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件。",
|
"accountActivationAndResendValidationEmailTip": "帳號啟用連結已經傳送到您的信箱地址:{email},如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件。",
|
||||||
"resendValidationEmailTip": "如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件到:{email}"
|
"resendValidationEmailTip": "如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件到:{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": "失敗次數超出最大限制,請稍後重試",
|
||||||
|
"repeated request": "重複的請求"
|
||||||
},
|
},
|
||||||
"parameter": {
|
"parameter": {
|
||||||
"id": "ID",
|
"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": "無法匯入交易",
|
||||||
"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": "批次替換選中的轉帳分類",
|
||||||
|
|||||||
@@ -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> {
|
function uploadTransactionPicture({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): Promise<TransactionPictureInfoBasicResponse> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
services.uploadTransactionPicture({ pictureFile, clientSessionId }).then(response => {
|
services.uploadTransactionPicture({ pictureFile, clientSessionId }).then(response => {
|
||||||
@@ -1243,6 +1268,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
parseImportDsvFile,
|
parseImportDsvFile,
|
||||||
parseImportTransaction,
|
parseImportTransaction,
|
||||||
importTransactions,
|
importTransactions,
|
||||||
|
getImportTransactionsProcess,
|
||||||
uploadTransactionPicture,
|
uploadTransactionPicture,
|
||||||
removeUnusedTransactionPicture,
|
removeUnusedTransactionPicture,
|
||||||
getTransactionPictureUrl,
|
getTransactionPictureUrl,
|
||||||
|
|||||||
@@ -786,7 +786,7 @@
|
|||||||
<v-btn color="teal" :disabled="submitting || !!editingTransaction || selectedImportTransactionCount < 1 || selectedInvalidTransactionCount > 0"
|
<v-btn color="teal" :disabled="submitting || !!editingTransaction || selectedImportTransactionCount < 1 || selectedInvalidTransactionCount > 0"
|
||||||
:append-icon="!submitting ? mdiArrowRight : undefined" @click="submit"
|
:append-icon="!submitting ? mdiArrowRight : undefined" @click="submit"
|
||||||
v-if="currentStep === 'checkData'">
|
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-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="secondary" variant="tonal"
|
<v-btn color="secondary" variant="tonal"
|
||||||
@@ -969,6 +969,7 @@ const fileInput = useTemplateRef<HTMLInputElement>('fileInput');
|
|||||||
const showState = ref<boolean>(false);
|
const showState = ref<boolean>(false);
|
||||||
const clientSessionId = ref<string>('');
|
const clientSessionId = ref<string>('');
|
||||||
const currentStep = ref<ImportTransactionDialogStep>('uploadFile');
|
const currentStep = ref<ImportTransactionDialogStep>('uploadFile');
|
||||||
|
const importProcess = ref<number>(0);
|
||||||
const fileType = ref<string>('ezbookkeeping');
|
const fileType = ref<string>('ezbookkeeping');
|
||||||
const fileSubType = ref<string>('ezbookkeeping_csv');
|
const fileSubType = ref<string>('ezbookkeeping_csv');
|
||||||
const fileEncoding = ref<string>('utf-8');
|
const fileEncoding = ref<string>('utf-8');
|
||||||
@@ -1927,6 +1928,7 @@ function open(): Promise<void> {
|
|||||||
fileSubType.value = 'ezbookkeeping_csv';
|
fileSubType.value = 'ezbookkeeping_csv';
|
||||||
fileEncoding.value = 'utf-8';
|
fileEncoding.value = 'utf-8';
|
||||||
currentStep.value = 'uploadFile';
|
currentStep.value = 'uploadFile';
|
||||||
|
importProcess.value = 0;
|
||||||
importFile.value = null;
|
importFile.value = null;
|
||||||
importData.value = '';
|
importData.value = '';
|
||||||
parsedFileData.value = undefined;
|
parsedFileData.value = undefined;
|
||||||
@@ -2221,10 +2223,48 @@ function submit(): void {
|
|||||||
editingTags.value = [];
|
editingTags.value = [];
|
||||||
submitting.value = true;
|
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({
|
transactionsStore.importTransactions({
|
||||||
transactions: transactions,
|
transactions: transactions,
|
||||||
clientSessionId: clientSessionId.value
|
clientSessionId: clientSessionId.value
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
if (showProcessTimer) {
|
||||||
|
importProcess.value = 0;
|
||||||
|
clearInterval(showProcessTimer);
|
||||||
|
showProcessTimer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
importedCount.value = response;
|
importedCount.value = response;
|
||||||
currentStep.value = 'finalResult';
|
currentStep.value = 'finalResult';
|
||||||
|
|
||||||
@@ -2235,6 +2275,12 @@ function submit(): void {
|
|||||||
|
|
||||||
submitting.value = false;
|
submitting.value = false;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
if (showProcessTimer) {
|
||||||
|
importProcess.value = 0;
|
||||||
|
clearInterval(showProcessTimer);
|
||||||
|
showProcessTimer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
submitting.value = false;
|
submitting.value = false;
|
||||||
|
|
||||||
if (!error.processed) {
|
if (!error.processed) {
|
||||||
|
|||||||
Reference in New Issue
Block a user