move all transactions from one account to another account (#288)

This commit is contained in:
MaysWind
2025-10-11 01:10:13 +08:00
parent 3ce7f6e99a
commit 2cb47bfd75
29 changed files with 974 additions and 41 deletions
+1
View File
@@ -353,6 +353,7 @@ func startWebServer(c *core.CliContext) error {
apiV1Route.GET("/transactions/get.json", bindApi(api.Transactions.TransactionGetHandler))
apiV1Route.POST("/transactions/add.json", bindApi(api.Transactions.TransactionCreateHandler))
apiV1Route.POST("/transactions/modify.json", bindApi(api.Transactions.TransactionModifyHandler))
apiV1Route.POST("/transactions/move/all.json", bindApi(api.Transactions.TransactionMoveAllBetweenAccountsHandler))
apiV1Route.POST("/transactions/delete.json", bindApi(api.Transactions.TransactionDeleteHandler))
if config.EnableDataImport {
+57
View File
@@ -1114,6 +1114,63 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.WebContext) (any, *er
return newTransactionResp, nil
}
// TransactionMoveAllBetweenAccountsHandler moves all transactions from one account to another account for current user
func (a *TransactionsApi) TransactionMoveAllBetweenAccountsHandler(c *core.WebContext) (any, *errs.Error) {
var transactionMoveReq models.TransactionMoveBetweenAccountsRequest
err := c.ShouldBindJSON(&transactionMoveReq)
if err != nil {
log.Warnf(c, "[transactions.TransactionMoveAllBetweenAccountsHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
if transactionMoveReq.FromAccountId == transactionMoveReq.ToAccountId {
return nil, errs.ErrCannotMoveTransactionToSameAccount
}
uid := c.GetCurrentUid()
accountMap, err := a.accounts.GetAccountsByAccountIds(c, uid, []int64{transactionMoveReq.FromAccountId, transactionMoveReq.ToAccountId})
if err != nil {
log.Errorf(c, "[transactions.TransactionMoveAllBetweenAccountsHandler] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
fromAccount, exists := accountMap[transactionMoveReq.FromAccountId]
if !exists {
return nil, errs.ErrSourceAccountNotFound
}
toAccount, exists := accountMap[transactionMoveReq.ToAccountId]
if !exists {
return nil, errs.ErrDestinationAccountNotFound
}
if fromAccount.Hidden || toAccount.Hidden {
return nil, errs.ErrCannotMoveTransactionFromOrToHiddenAccount
}
if fromAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS || toAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
return nil, errs.ErrCannotMoveTransactionFromOrToParentAccount
}
if fromAccount.Currency != toAccount.Currency {
return nil, errs.ErrCannotMoveTransactionBetweenAccountsWithDifferentCurrencies
}
err = a.transactions.MoveAllTransactionsBetweenAccounts(c, uid, transactionMoveReq.FromAccountId, transactionMoveReq.ToAccountId)
if err != nil {
log.Errorf(c, "[transactions.TransactionMoveAllBetweenAccountsHandler] failed to move all transactions from account \"id:%d\" to account \"id:%d\" for user \"uid:%d\", because %s", transactionMoveReq.FromAccountId, transactionMoveReq.ToAccountId, uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.Infof(c, "[transactions.TransactionMoveAllBetweenAccountsHandler] user \"uid:%d\" has moved all transactions from account \"id:%d\" to account \"id:%d\" successfully", uid, transactionMoveReq.FromAccountId, transactionMoveReq.ToAccountId)
return true, nil
}
// TransactionDeleteHandler deletes an existed transaction by request parameters for current user
func (a *TransactionsApi) TransactionDeleteHandler(c *core.WebContext) (any, *errs.Error) {
var transactionDeleteReq models.TransactionDeleteRequest
+41 -37
View File
@@ -4,41 +4,45 @@ import "net/http"
// Error codes related to transaction
var (
ErrTransactionIdInvalid = NewNormalError(NormalSubcategoryTransaction, 0, http.StatusBadRequest, "transaction id is invalid")
ErrTransactionNotFound = NewNormalError(NormalSubcategoryTransaction, 1, http.StatusBadRequest, "transaction not found")
ErrTransactionTypeInvalid = NewNormalError(NormalSubcategoryTransaction, 2, http.StatusBadRequest, "transaction type is invalid")
ErrTransactionSourceAndDestinationIdCannotBeEqual = NewNormalError(NormalSubcategoryTransaction, 3, http.StatusBadRequest, "transaction source and destination account id cannot be equal")
ErrTransactionSourceAndDestinationAmountNotEqual = NewNormalError(NormalSubcategoryTransaction, 4, http.StatusBadRequest, "transaction source and destination amount not equal")
ErrTransactionDestinationAccountCannotBeSet = NewNormalError(NormalSubcategoryTransaction, 5, http.StatusBadRequest, "transaction destination account cannot be set")
ErrTransactionDestinationAmountCannotBeSet = NewNormalError(NormalSubcategoryTransaction, 6, http.StatusBadRequest, "transaction destination amount cannot be set")
ErrTooMuchTransactionInOneSecond = NewNormalError(NormalSubcategoryTransaction, 7, http.StatusBadRequest, "too much transaction in one second")
ErrBalanceModificationTransactionCannotSetCategory = NewNormalError(NormalSubcategoryTransaction, 8, http.StatusBadRequest, "balance modification transaction cannot set category")
ErrBalanceModificationTransactionCannotChangeAccountId = NewNormalError(NormalSubcategoryTransaction, 9, http.StatusBadRequest, "balance modification transaction cannot change account id")
ErrBalanceModificationTransactionCannotAddWhenNotEmpty = NewNormalError(NormalSubcategoryTransaction, 10, http.StatusBadRequest, "balance modification transaction cannot add when other transaction exists")
ErrCannotAddTransactionToHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 11, http.StatusBadRequest, "cannot add transaction to hidden account")
ErrCannotModifyTransactionInHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 12, http.StatusBadRequest, "cannot modify transaction of hidden account")
ErrCannotDeleteTransactionInHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 13, http.StatusBadRequest, "cannot delete transaction in hidden account")
ErrCannotAddTransactionToParentAccount = NewNormalError(NormalSubcategoryTransaction, 14, http.StatusBadRequest, "cannot add transaction to parent account")
ErrCannotModifyTransactionInParentAccount = NewNormalError(NormalSubcategoryTransaction, 15, http.StatusBadRequest, "cannot modify transaction of parent account")
ErrCannotDeleteTransactionInParentAccount = NewNormalError(NormalSubcategoryTransaction, 16, http.StatusBadRequest, "cannot delete transaction in parent account")
ErrCannotCreateTransactionWithThisTransactionTime = NewNormalError(NormalSubcategoryTransaction, 17, http.StatusBadRequest, "cannot add transaction with this transaction time")
ErrCannotModifyTransactionWithThisTransactionTime = NewNormalError(NormalSubcategoryTransaction, 18, http.StatusBadRequest, "cannot modify transaction with this transaction time")
ErrCannotDeleteTransactionWithThisTransactionTime = NewNormalError(NormalSubcategoryTransaction, 19, http.StatusBadRequest, "cannot delete transaction with this transaction time")
ErrCannotUseHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 20, http.StatusBadRequest, "cannot use hidden account")
ErrCannotUseHiddenTransactionCategory = NewNormalError(NormalSubcategoryTransaction, 21, http.StatusBadRequest, "cannot use hidden transaction category")
ErrCannotUseHiddenTransactionTag = NewNormalError(NormalSubcategoryTransaction, 22, http.StatusBadRequest, "cannot use hidden transaction tag")
ErrTransactionHasTooManyTags = NewNormalError(NormalSubcategoryTransaction, 23, http.StatusBadRequest, "transaction has too many tags")
ErrTransactionHasTooManyPictures = NewNormalError(NormalSubcategoryTransaction, 24, http.StatusBadRequest, "transaction has too many pictures")
ErrImportFileTypeIsEmpty = NewNormalError(NormalSubcategoryTransaction, 25, http.StatusBadRequest, "import file type is empty")
ErrImportFileTypeNotSupported = NewNormalError(NormalSubcategoryTransaction, 26, http.StatusBadRequest, "import file type not supported")
ErrNoDataToImport = NewNormalError(NormalSubcategoryTransaction, 27, http.StatusBadRequest, "no data to import")
ErrCannotAddTransactionBeforeBalanceModificationTransaction = NewNormalError(NormalSubcategoryTransaction, 28, http.StatusBadRequest, "cannot add transaction before balance modification transaction")
ErrBalanceModificationTransactionCannotModifyTime = NewNormalError(NormalSubcategoryTransaction, 29, http.StatusBadRequest, "balance modification transaction cannot modify transaction time")
ErrTransferTransactionAmountCannotBeLessThanZero = NewNormalError(NormalSubcategoryTransaction, 30, http.StatusBadRequest, "transfer transaction amount cannot be less than zero")
ErrImportFileEncodingIsEmpty = NewNormalError(NormalSubcategoryTransaction, 31, http.StatusBadRequest, "import file encoding is empty")
ErrImportFileEncodingNotSupported = NewNormalError(NormalSubcategoryTransaction, 32, http.StatusBadRequest, "import file encoding not supported")
ErrImportFileColumnMappingInvalid = NewNormalError(NormalSubcategoryTransaction, 33, http.StatusBadRequest, "column mapping invalid")
ErrImportFileTransactionTypeMappingInvalid = NewNormalError(NormalSubcategoryTransaction, 34, http.StatusBadRequest, "transaction type mapping invalid")
ErrImportFileTransactionTimeFormatInvalid = NewNormalError(NormalSubcategoryTransaction, 35, http.StatusBadRequest, "transaction time format invalid")
ErrImportFileTransactionTimezoneFormatInvalid = NewNormalError(NormalSubcategoryTransaction, 36, http.StatusBadRequest, "transaction time zone format invalid")
ErrTransactionIdInvalid = NewNormalError(NormalSubcategoryTransaction, 0, http.StatusBadRequest, "transaction id is invalid")
ErrTransactionNotFound = NewNormalError(NormalSubcategoryTransaction, 1, http.StatusBadRequest, "transaction not found")
ErrTransactionTypeInvalid = NewNormalError(NormalSubcategoryTransaction, 2, http.StatusBadRequest, "transaction type is invalid")
ErrTransactionSourceAndDestinationIdCannotBeEqual = NewNormalError(NormalSubcategoryTransaction, 3, http.StatusBadRequest, "transaction source and destination account id cannot be equal")
ErrTransactionSourceAndDestinationAmountNotEqual = NewNormalError(NormalSubcategoryTransaction, 4, http.StatusBadRequest, "transaction source and destination amount not equal")
ErrTransactionDestinationAccountCannotBeSet = NewNormalError(NormalSubcategoryTransaction, 5, http.StatusBadRequest, "transaction destination account cannot be set")
ErrTransactionDestinationAmountCannotBeSet = NewNormalError(NormalSubcategoryTransaction, 6, http.StatusBadRequest, "transaction destination amount cannot be set")
ErrTooMuchTransactionInOneSecond = NewNormalError(NormalSubcategoryTransaction, 7, http.StatusBadRequest, "too much transaction in one second")
ErrBalanceModificationTransactionCannotSetCategory = NewNormalError(NormalSubcategoryTransaction, 8, http.StatusBadRequest, "balance modification transaction cannot set category")
ErrBalanceModificationTransactionCannotChangeAccountId = NewNormalError(NormalSubcategoryTransaction, 9, http.StatusBadRequest, "balance modification transaction cannot change account id")
ErrBalanceModificationTransactionCannotAddWhenNotEmpty = NewNormalError(NormalSubcategoryTransaction, 10, http.StatusBadRequest, "balance modification transaction cannot add when other transaction exists")
ErrCannotAddTransactionToHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 11, http.StatusBadRequest, "cannot add transaction to hidden account")
ErrCannotModifyTransactionInHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 12, http.StatusBadRequest, "cannot modify transaction of hidden account")
ErrCannotDeleteTransactionInHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 13, http.StatusBadRequest, "cannot delete transaction in hidden account")
ErrCannotAddTransactionToParentAccount = NewNormalError(NormalSubcategoryTransaction, 14, http.StatusBadRequest, "cannot add transaction to parent account")
ErrCannotModifyTransactionInParentAccount = NewNormalError(NormalSubcategoryTransaction, 15, http.StatusBadRequest, "cannot modify transaction of parent account")
ErrCannotDeleteTransactionInParentAccount = NewNormalError(NormalSubcategoryTransaction, 16, http.StatusBadRequest, "cannot delete transaction in parent account")
ErrCannotCreateTransactionWithThisTransactionTime = NewNormalError(NormalSubcategoryTransaction, 17, http.StatusBadRequest, "cannot add transaction with this transaction time")
ErrCannotModifyTransactionWithThisTransactionTime = NewNormalError(NormalSubcategoryTransaction, 18, http.StatusBadRequest, "cannot modify transaction with this transaction time")
ErrCannotDeleteTransactionWithThisTransactionTime = NewNormalError(NormalSubcategoryTransaction, 19, http.StatusBadRequest, "cannot delete transaction with this transaction time")
ErrCannotUseHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 20, http.StatusBadRequest, "cannot use hidden account")
ErrCannotUseHiddenTransactionCategory = NewNormalError(NormalSubcategoryTransaction, 21, http.StatusBadRequest, "cannot use hidden transaction category")
ErrCannotUseHiddenTransactionTag = NewNormalError(NormalSubcategoryTransaction, 22, http.StatusBadRequest, "cannot use hidden transaction tag")
ErrTransactionHasTooManyTags = NewNormalError(NormalSubcategoryTransaction, 23, http.StatusBadRequest, "transaction has too many tags")
ErrTransactionHasTooManyPictures = NewNormalError(NormalSubcategoryTransaction, 24, http.StatusBadRequest, "transaction has too many pictures")
ErrImportFileTypeIsEmpty = NewNormalError(NormalSubcategoryTransaction, 25, http.StatusBadRequest, "import file type is empty")
ErrImportFileTypeNotSupported = NewNormalError(NormalSubcategoryTransaction, 26, http.StatusBadRequest, "import file type not supported")
ErrNoDataToImport = NewNormalError(NormalSubcategoryTransaction, 27, http.StatusBadRequest, "no data to import")
ErrCannotAddTransactionBeforeBalanceModificationTransaction = NewNormalError(NormalSubcategoryTransaction, 28, http.StatusBadRequest, "cannot add transaction before balance modification transaction")
ErrBalanceModificationTransactionCannotModifyTime = NewNormalError(NormalSubcategoryTransaction, 29, http.StatusBadRequest, "balance modification transaction cannot modify transaction time")
ErrTransferTransactionAmountCannotBeLessThanZero = NewNormalError(NormalSubcategoryTransaction, 30, http.StatusBadRequest, "transfer transaction amount cannot be less than zero")
ErrImportFileEncodingIsEmpty = NewNormalError(NormalSubcategoryTransaction, 31, http.StatusBadRequest, "import file encoding is empty")
ErrImportFileEncodingNotSupported = NewNormalError(NormalSubcategoryTransaction, 32, http.StatusBadRequest, "import file encoding not supported")
ErrImportFileColumnMappingInvalid = NewNormalError(NormalSubcategoryTransaction, 33, http.StatusBadRequest, "column mapping invalid")
ErrImportFileTransactionTypeMappingInvalid = NewNormalError(NormalSubcategoryTransaction, 34, http.StatusBadRequest, "transaction type mapping invalid")
ErrImportFileTransactionTimeFormatInvalid = NewNormalError(NormalSubcategoryTransaction, 35, http.StatusBadRequest, "transaction time format invalid")
ErrImportFileTransactionTimezoneFormatInvalid = NewNormalError(NormalSubcategoryTransaction, 36, http.StatusBadRequest, "transaction time zone format invalid")
ErrCannotMoveTransactionToSameAccount = NewNormalError(NormalSubcategoryTransaction, 37, http.StatusBadRequest, "cannot move transaction to same account")
ErrCannotMoveTransactionFromOrToHiddenAccount = NewNormalError(NormalSubcategoryTransaction, 38, http.StatusBadRequest, "cannot move transaction from or to hidden account")
ErrCannotMoveTransactionFromOrToParentAccount = NewNormalError(NormalSubcategoryTransaction, 39, http.StatusBadRequest, "cannot move transaction from or to parent account")
ErrCannotMoveTransactionBetweenAccountsWithDifferentCurrencies = NewNormalError(NormalSubcategoryTransaction, 40, http.StatusBadRequest, "cannot move transaction between accounts with different currencies")
)
+6
View File
@@ -279,6 +279,12 @@ type TransactionGetRequest struct {
TrimTag bool `form:"trim_tag"`
}
// TransactionMoveBetweenAccountsRequest represents all parameters of moving all transactions between accounts request
type TransactionMoveBetweenAccountsRequest struct {
FromAccountId int64 `json:"fromAccountId,string" binding:"required,min=1"`
ToAccountId int64 `json:"toAccountId,string" binding:"required,min=1"`
}
// TransactionDeleteRequest represents all parameters of transaction deleting request
type TransactionDeleteRequest struct {
Id int64 `json:"id,string" binding:"required,min=1"`
+213
View File
@@ -1168,6 +1168,219 @@ func (s *TransactionService) ModifyTransaction(c core.Context, transaction *mode
return nil
}
func (s *TransactionService) MoveAllTransactionsBetweenAccounts(c core.Context, uid int64, fromAccountId int64, toAccountId int64) error {
if uid <= 0 {
return errs.ErrUserIdInvalid
}
if fromAccountId <= 0 || toAccountId <= 0 {
return errs.ErrAccountIdInvalid
}
if fromAccountId == toAccountId {
return errs.ErrCannotMoveTransactionToSameAccount
}
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
// get and verify from and to account
fromAccount := &models.Account{}
has, err := sess.ID(fromAccountId).Where("uid=? AND deleted=?", uid, false).Get(fromAccount)
if err != nil {
return err
} else if !has {
return errs.ErrAccountNotFound
}
toAccount := &models.Account{}
has, err = sess.ID(toAccountId).Where("uid=? AND deleted=?", uid, false).Get(toAccount)
if err != nil {
return err
} else if !has {
return errs.ErrAccountNotFound
}
if fromAccount.Hidden || toAccount.Hidden {
return errs.ErrCannotMoveTransactionFromOrToHiddenAccount
}
if fromAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS || toAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
return errs.ErrCannotMoveTransactionFromOrToParentAccount
}
if fromAccount.Currency != toAccount.Currency {
return errs.ErrCannotMoveTransactionBetweenAccountsWithDifferentCurrencies
}
// combine balance modification transaction
var balanceModificationTransactions []*models.Transaction
err = sess.Where("uid=? AND deleted=? AND type=? AND (account_id=? OR account_id=?)", uid, false, models.TRANSACTION_DB_TYPE_MODIFY_BALANCE, fromAccountId, toAccountId).Find(&balanceModificationTransactions)
if err != nil {
return err
}
if len(balanceModificationTransactions) > 2 {
log.Errorf(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has more than 2 balance modification transactions in account \"id:%d\" and account \"id:%d\", cannot combine balance modification transaction", uid, fromAccountId, toAccountId)
return errs.ErrOperationFailed
} else if len(balanceModificationTransactions) == 2 && balanceModificationTransactions[0].AccountId != balanceModificationTransactions[1].AccountId {
// if two balance modification transactions exist, merge the amounts into the earlier one and delete the later transaction
var earlierTransaction *models.Transaction
var laterTransaction *models.Transaction
if balanceModificationTransactions[0].TransactionTime < balanceModificationTransactions[1].TransactionTime {
earlierTransaction = balanceModificationTransactions[0]
laterTransaction = balanceModificationTransactions[1]
} else {
earlierTransaction = balanceModificationTransactions[1]
laterTransaction = balanceModificationTransactions[0]
}
earlierTransaction.Amount += laterTransaction.Amount
earlierTransaction.RelatedAccountAmount += laterTransaction.RelatedAccountAmount
earlierTransaction.UpdatedUnixTime = time.Now().Unix()
updatedRows, err := sess.ID(earlierTransaction.TransactionId).Cols("amount", "related_account_amount", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(earlierTransaction)
if err != nil {
return err
} else if updatedRows < 1 {
log.Errorf(c, "[transactions.MoveAllTransactionsBetweenAccounts] failed to update earlier balance modification transaction")
return errs.ErrDatabaseOperationFailed
}
laterTransaction.Deleted = true
laterTransaction.DeletedUnixTime = time.Now().Unix()
deletedRows, err := sess.ID(laterTransaction.TransactionId).Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).Update(laterTransaction)
if err != nil {
return err
} else if deletedRows < 1 {
log.Errorf(c, "[transactions.MoveAllTransactionsBetweenAccounts] failed to delete later balance modification transaction")
return errs.ErrDatabaseOperationFailed
}
log.Infof(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has combined two balance modification transactions \"id:%d\" and \"id:%d\", retained transaction is \"id:%d\"", uid, earlierTransaction.TransactionId, laterTransaction.TransactionId, earlierTransaction.TransactionId)
} else if len(balanceModificationTransactions) == 1 {
// when merging a new balance modification transaction, if its date is later than the account's earliest transaction, update the balance modification transaction time accordingly
anotherAccountId := int64(0)
if balanceModificationTransactions[0].AccountId == fromAccountId {
anotherAccountId = toAccountId
} else if balanceModificationTransactions[0].AccountId == toAccountId {
anotherAccountId = fromAccountId
} else {
log.Errorf(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has a balance modification transaction \"id:%d\" which account id is neither \"%d\" nor \"%d\"", uid, balanceModificationTransactions[0].TransactionId, fromAccountId, toAccountId)
return errs.ErrOperationFailed
}
earliestTransaction := &models.Transaction{}
has, err := sess.Where("uid=? AND deleted=? AND account_id=?", uid, false, anotherAccountId).OrderBy("transaction_time asc").Limit(1).Get(earliestTransaction)
if err != nil {
return err
} else if has && balanceModificationTransactions[0].TransactionTime > earliestTransaction.TransactionTime {
balanceModificationTransaction := balanceModificationTransactions[0]
balanceModificationTransaction.TransactionTime = utils.GetMinTransactionTimeFromUnixTime(utils.GetUnixTimeFromTransactionTime(earliestTransaction.TransactionTime) - 1)
balanceModificationTransaction.UpdatedUnixTime = time.Now().Unix()
if balanceModificationTransaction.TransactionTime < 0 {
balanceModificationTransaction.TransactionTime = 0
}
updatedRows, err := sess.ID(balanceModificationTransaction.TransactionId).Cols("transaction_time", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(balanceModificationTransaction)
if err != nil {
return err
} else if updatedRows < 1 {
log.Errorf(c, "[transactions.MoveAllTransactionsBetweenAccounts] failed to update balance modification transaction time")
return errs.ErrDatabaseOperationFailed
}
log.Infof(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has updated balance modification transaction \"id:%d\" time to %d, because earliest transaction time in account \"id:%d\" is %d", uid, balanceModificationTransaction.TransactionId, balanceModificationTransaction.TransactionTime, toAccountId, earliestTransaction.TransactionTime)
}
}
// update all transactions of from account
updateModel := &models.Transaction{
AccountId: toAccountId,
UpdatedUnixTime: time.Now().Unix(),
}
updatedRows, err := sess.Cols("account_id", "updated_unix_time").Where("uid=? AND deleted=? AND account_id=?", uid, false, fromAccountId).Update(updateModel)
if err != nil {
return err
}
if updatedRows > 0 {
log.Infof(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has moved %d transactions from account \"id:%d\" to account \"id:%d\"", uid, updatedRows, fromAccountId, toAccountId)
}
// update all related transactions of from account
updateRelatedModel := &models.Transaction{
RelatedAccountId: toAccountId,
UpdatedUnixTime: time.Now().Unix(),
}
relatedUpdatedRows, err := sess.Cols("related_account_id", "updated_unix_time").Where("uid=? AND deleted=? AND related_account_id=?", uid, false, fromAccountId).Update(updateRelatedModel)
if err != nil {
return err
}
if updatedRows > 0 {
log.Infof(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has moved %d related transactions from account \"id:%d\" to account \"id:%d\"", uid, relatedUpdatedRows, fromAccountId, toAccountId)
}
// delete all transfer transactions which related account id and account id are both
deletedModel := &models.Transaction{
Deleted: true,
DeletedUnixTime: time.Now().Unix(),
}
deletedRows, err := sess.Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=? AND (type=? OR type=?) AND account_id=? AND related_account_id=?", uid, false, models.TRANSACTION_DB_TYPE_TRANSFER_OUT, models.TRANSACTION_DB_TYPE_TRANSFER_IN, toAccountId, toAccountId).Update(deletedModel)
if err != nil {
return err
}
if deletedRows > 0 {
log.Infof(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has deleted %d transactions which account id and related account id are both \"%d\"", uid, deletedRows, toAccountId)
}
// update account balance
if fromAccount.Balance != 0 {
toAccount.UpdatedUnixTime = time.Now().Unix()
updatedRows, err := sess.ID(toAccount.AccountId).SetExpr("balance", fmt.Sprintf("balance+(%d)", fromAccount.Balance)).Cols("updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(toAccount)
if err != nil {
return err
} else if updatedRows < 1 {
log.Errorf(c, "[transactions.MoveAllTransactionsBetweenAccounts] failed to update to account balance")
return errs.ErrDatabaseOperationFailed
}
log.Infof(c, "[transactions.MoveAllTransactionsBetweenAccounts] user \"uid:%d\" has updated account \"id:%d\" balance from %d to %d", uid, toAccountId, toAccount.Balance, toAccount.Balance+fromAccount.Balance)
fromAccount.Balance = 0
fromAccount.UpdatedUnixTime = time.Now().Unix()
updatedRows, err = sess.ID(fromAccount.AccountId).Cols("balance", "updated_unix_time").Where("uid=? AND deleted=?", fromAccount.Uid, false).Update(fromAccount)
if err != nil {
return err
} else if updatedRows < 1 {
log.Errorf(c, "[transactions.MoveAllTransactionsBetweenAccounts] failed to update from account balance")
return errs.ErrDatabaseOperationFailed
}
}
return nil
})
}
// DeleteTransaction deletes an existed transaction from database
func (s *TransactionService) DeleteTransaction(c core.Context, uid int64, transactionId int64) error {
if uid <= 0 {
+4
View File
@@ -61,6 +61,7 @@ import type {
import type {
TransactionCreateRequest,
TransactionModifyRequest,
TransactionMoveBetweenAccountsRequest,
TransactionDeleteRequest,
TransactionImportRequest,
TransactionListByMaxTimeRequest,
@@ -528,6 +529,9 @@ export default {
modifyTransaction: (req: TransactionModifyRequest): ApiResponsePromise<TransactionInfoResponse> => {
return axios.post<ApiResponse<TransactionInfoResponse>>('v1/transactions/modify.json', req);
},
moveAllTransactionsBetweenAccounts: (req: TransactionMoveBetweenAccountsRequest): ApiResponsePromise<boolean> => {
return axios.post<ApiResponse<boolean>>('v1/transactions/move/all.json', req);
},
deleteTransaction: (req: TransactionDeleteRequest): ApiResponsePromise<boolean> => {
return axios.post<ApiResponse<boolean>>('v1/transactions/delete.json', req);
},
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Sind Sie sicher, dass Sie {count} Transaktionen importieren möchten?",
"importingTransactions": "Importing ({process}%)",
"importTransactionResult": "Sie haben {count} Transaktionen erfolgreich importiert.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"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."
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Transaction type mapping is invalid",
"transaction time format invalid": "Transaction time format is invalid",
"transaction time zone format invalid": "Transaction time zone format is invalid",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "Transaktionskategorie-ID ist ungültig",
"transaction category not found": "Transaktionskategorie nicht gefunden",
"transaction category type is invalid": "Transaktionskategorietyp ist ungültig",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
"Unable to delete this account": "Konto kann nicht gelöscht werden",
"Unable to delete this sub-account": "Unable to delete this sub-account",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Default Transfer Category",
"Invalid Account": "Ungültiges Konto",
"Target Account": "Zielkonto",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Ungültiges Tag",
"Target Tag": "Ziel-Tag",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Are you sure you want to import {count} transactions?",
"importingTransactions": "Importing ({process}%)",
"importTransactionResult": "You have imported {count} transactions successfully.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"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}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Transaction type mapping is invalid",
"transaction time format invalid": "Transaction time format is invalid",
"transaction time zone format invalid": "Transaction time zone format is invalid",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "Transaction category ID is invalid",
"transaction category not found": "Transaction category is not found",
"transaction category type is invalid": "Transaction category type is invalid",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
"Unable to delete this account": "Unable to delete this account",
"Unable to delete this sub-account": "Unable to delete this sub-account",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Default Transfer Category",
"Invalid Account": "Invalid Account",
"Target Account": "Target Account",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Invalid Tag",
"Target Tag": "Target Tag",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "¿Está seguro de que desea importar {count} transacciones?",
"importingTransactions": "Importing ({process}%)",
"importTransactionResult": "Ha importado {count} transacciones correctamente.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"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}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Transaction type mapping is invalid",
"transaction time format invalid": "Transaction time format is invalid",
"transaction time zone format invalid": "Transaction time zone format is invalid",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "El ID de categoría de transacción no es válido",
"transaction category not found": "No se encuentra la categoría de transacción",
"transaction category type is invalid": "El tipo de categoría de transacción no es válido",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
"Unable to delete this account": "No se puede eliminar esta cuenta",
"Unable to delete this sub-account": "Unable to delete this sub-account",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Default Transfer Category",
"Invalid Account": "Cuenta no válida",
"Target Account": "Cuenta de destino",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Etiqueta no válida",
"Target Tag": "Etiqueta de destino",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Êtes-vous sûr de vouloir importer {count} transactions ?",
"importingTransactions": "Importation ({process}%)",
"importTransactionResult": "Vous avez importé {count} transactions avec succès.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"accountActivationAndResendValidationEmailTip": "Le lien d'activation du compte a été envoyé à votre adresse e-mail : {email}, Si vous ne recevez pas le mail, veuillez remplir à nouveau le mot de passe et cliquer sur le bouton ci-dessous pour renvoyer l'e-mail de validation.",
"resendValidationEmailTip": "Si vous ne recevez pas le mail, veuillez remplir à nouveau le mot de passe et cliquer sur le bouton ci-dessous pour renvoyer l'e-mail de validation à : {email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Le mappage du type de transaction est invalide",
"transaction time format invalid": "Le format d'heure de transaction est invalide",
"transaction time zone format invalid": "Le format de fuseau horaire de transaction est invalide",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "L'ID de catégorie de transaction est invalide",
"transaction category not found": "Catégorie de transaction non trouvée",
"transaction category type is invalid": "Le type de catégorie de transaction est invalide",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Êtes-vous sûr de vouloir supprimer ce sous-compte ?",
"Unable to delete this account": "Impossible de supprimer ce compte",
"Unable to delete this sub-account": "Impossible de supprimer ce sous-compte",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Relevé de rapprochement",
"Account Balance Trends": "Tendances du solde du compte",
"Update Closing Balance": "Mettre à jour le solde de clôture",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Catégorie de virement par défaut",
"Invalid Account": "Compte invalide",
"Target Account": "Compte cible",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Étiquette invalide",
"Target Tag": "Étiquette cible",
"Remove Tag": "Supprimer l'étiquette",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Sei sicuro di voler importare {count} transazioni?",
"importingTransactions": "Importing ({process}%)",
"importTransactionResult": "Hai importato {count} transazioni.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"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}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Mappatura del tipo di transazione non valida",
"transaction time format invalid": "Formato dell'ora della transazione non valido",
"transaction time zone format invalid": "Formato del fuso orario della transazione non valido",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "ID categoria transazione non valido",
"transaction category not found": "Categoria transazione non trovata",
"transaction category type is invalid": "Tipo di categoria transazione non valido",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Sei sicuro di voler eliminare questo sotto-account?",
"Unable to delete this account": "Impossibile eliminare questo account",
"Unable to delete this sub-account": "Impossibile eliminare questo sotto-account",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Categoria di trasferimento predefinita",
"Invalid Account": "Conto non valido",
"Target Account": "Conto di destinazione",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Tag non valido",
"Target Tag": "Tag di destinazione",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "本当に{count}件の取引をインポートしますか?",
"importingTransactions": "Importing ({process}%)",
"importTransactionResult": "{count}件の取引を正常にインポートしました。",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"accountActivationAndResendValidationEmailTip": "アカウントの有効化リンクがメールアドレスに送信されました:{email}、メールが届かない場合はパスワードをもう一度入力して下のボタンをクリックして認証メールを再送信してください。",
"resendValidationEmailTip": "メールが届かない場合は、パスワードをもう一度入力の上、以下のボタンをクリックして検証メールを再送信してください: {email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "取引タイプのマッピングが無効です",
"transaction time format invalid": "取引時間の形式が無効です",
"transaction time zone format invalid": "取引のタイムゾーン形式が無効です",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "取引カテゴリIDは無効です",
"transaction category not found": "取引カテゴリは見つかりません",
"transaction category type is invalid": "取引カテゴリタイプは無効です",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
"Unable to delete this account": "この口座を削除できません",
"Unable to delete this sub-account": "Unable to delete this sub-account",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Default Transfer Category",
"Invalid Account": "無効な口座",
"Target Account": "対象口座",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "無効なタグ",
"Target Tag": "対象タグ",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "{count}개의 거래를 가져오시겠습니까?",
"importingTransactions": "가져오는 중 ({process}%)",
"importTransactionResult": "성공적으로 {count}개의 거래를 가져왔습니다.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "이 작업은 되돌릴 수 없습니다. {account}의 거래 데이터를 지웁니다. 계속하시려면 현재 비밀번호를 입력하세요.",
"accountActivationAndResendValidationEmailTip": "계정 활성화 링크가 귀하의 이메일 주소({email})로 전송되었습니다. 메일을 받지 못하신 경우, 비밀번호를 다시 입력하고 아래 버튼을 클릭하여 확인 메일을 재전송하십시오.",
"resendValidationEmailTip": "메일을 받지 못하신 경우, 비밀번호를 다시 입력하고 아래 버튼을 클릭하여 확인 메일을 {email}로 재전송하십시오."
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "거래 유형 매핑이 유효하지 않습니다.",
"transaction time format invalid": "거래 시간 형식이 유효하지 않습니다.",
"transaction time zone format invalid": "거래 시간대 형식이 유효하지 않습니다.",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "거래 카테고리 ID가 유효하지 않습니다.",
"transaction category not found": "거래 카테고리를 찾을 수 없습니다.",
"transaction category type is invalid": "거래 카테고리 유형이 유효하지 않습니다.",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "이 하위계좌를 삭제하시겠습니까?",
"Unable to delete this account": "이 계좌를 삭제할 수 없습니다.",
"Unable to delete this sub-account": "이 하위계좌를 삭제할 수 없습니다.",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "조정 명세서",
"Account Balance Trends": "계좌 잔액 추세",
"Update Closing Balance": "마감 잔액 업데이트",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "기본 이체 카테고리",
"Invalid Account": "유효하지 않은 계좌",
"Target Account": "대상 계좌",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "유효하지 않은 태그",
"Target Tag": "대상 태그",
"Remove Tag": "태그 제거",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Weet je zeker dat je {count} transacties wilt importeren?",
"importingTransactions": "Bezig met importeren ({process}%)",
"importTransactionResult": "Je hebt {count} transacties succesvol geïmporteerd.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"accountActivationAndResendValidationEmailTip": "Een activatielink is verzonden naar je e-mailadres: {email}. Als je de e-mail niet ontvangt, vul dan je wachtwoord opnieuw in en klik op de knop hieronder om de validatiemail opnieuw te verzenden.",
"resendValidationEmailTip": "Als je de e-mail niet ontvangt, vul dan je wachtwoord opnieuw in en klik op de knop hieronder om de validatiemail opnieuw te verzenden naar: {email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Transactietypetoewijzing is ongeldig",
"transaction time format invalid": "Formaat van transactietijd is ongeldig",
"transaction time zone format invalid": "Formaat van transactietijdzone is ongeldig",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "Transactiecategorie-ID is ongeldig",
"transaction category not found": "Transactiecategorie niet gevonden",
"transaction category type is invalid": "Type transactiecategorie is ongeldig",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Weet je zeker dat je deze subrekening wilt verwijderen?",
"Unable to delete this account": "Kan deze rekening niet verwijderen",
"Unable to delete this sub-account": "Kan deze subrekening niet verwijderen",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Afstemmingsrapport",
"Account Balance Trends": "Rekening-saldotrends",
"Update Closing Balance": "Eindsaldo bijwerken",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Standaard overboekingscategorie",
"Invalid Account": "Ongeldige rekening",
"Target Account": "Doelrekening",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Ongeldige tag",
"Target Tag": "Doeltag",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Tem certeza de que deseja importar {count} transações?",
"importingTransactions": "Importando ({process}%)",
"importTransactionResult": "Você importou {count} transações com sucesso.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"accountActivationAndResendValidationEmailTip": "O link de ativação da conta foi enviado para seu endereço de e-mail: {email}. Se você não receber o e-mail, por favor preencha a senha novamente e clique no botão abaixo para reenviar o e-mail de validação.",
"resendValidationEmailTip": "Se você não receber o e-mail, por favor preencha a senha novamente e clique no botão abaixo para reenviar o e-mail de validação para: {email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Mapeamento de tipo de transação é inválido",
"transaction time format invalid": "Formato de tempo da transação é inválido",
"transaction time zone format invalid": "Formato de fuso horário da transação é inválido",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "ID de categoria de transação é inválido",
"transaction category not found": "Categoria de transação não encontrada",
"transaction category type is invalid": "Tipo de categoria de transação é inválido",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Tem certeza de que deseja deletar esta subconta?",
"Unable to delete this account": "Não foi possível deletar esta conta",
"Unable to delete this sub-account": "Não foi possível deletar esta subconta",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Categoria Padrão de Transferência",
"Invalid Account": "Conta Inválida",
"Target Account": "Conta Alvo",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Tag Inválida",
"Target Tag": "Tag Alvo",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Вы уверены, что хотите импортировать {count} транзакций?",
"importingTransactions": "Importing ({process}%)",
"importTransactionResult": "Вы успешно импортировали {count} транзакций.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"accountActivationAndResendValidationEmailTip": "Ссылка для активации учетной записи была отправлена на ваш электронный адрес: {email}. Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно.",
"resendValidationEmailTip": "Если вы не получили письмо, заполните пароль снова и нажмите кнопку ниже, чтобы отправить письмо повторно на: {email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Transaction type mapping is invalid",
"transaction time format invalid": "Transaction time format is invalid",
"transaction time zone format invalid": "Transaction time zone format is invalid",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "ID категории транзакции недействителен",
"transaction category not found": "Категория транзакции не найдена",
"transaction category type is invalid": "Тип категории транзакции недействителен",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
"Unable to delete this account": "Не удалось удалить этот счет",
"Unable to delete this sub-account": "Unable to delete this sub-account",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Default Transfer Category",
"Invalid Account": "Недействительный счет",
"Target Account": "Целевой счет",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Недействительный тег",
"Target Tag": "Целевой тег",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "คุณแน่ใจหรือไม่ว่าต้องการนำเข้าธุรกรรม {count} รายการ?",
"importingTransactions": "กำลังนำเข้า ({process}%)",
"importTransactionResult": "คุณได้นำเข้าธุรกรรม {count} รายการเรียบร้อยแล้ว",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "คุณไม่สามารถยกเลิกการกระทำนี้ได้ การกระทำนี้จะลบข้อมูลธุรกรรมทั้งหมดใน {account} โปรดป้อนรหัสผ่านปัจจุบันเพื่อยืนยัน",
"accountActivationAndResendValidationEmailTip": "ลิงก์สำหรับเปิดใช้งานบัญชีได้ถูกส่งไปยังอีเมลของคุณแล้ว: {email} หากคุณไม่ได้รับอีเมล โปรดกรอกรหัสผ่านอีกครั้งแล้วกดปุ่มด้านล่างเพื่อส่งอีเมลยืนยันอีกครั้ง",
"resendValidationEmailTip": "หากคุณไม่ได้รับอีเมล โปรดกรอกรหัสผ่านอีกครั้งแล้วกดปุ่มด้านล่างเพื่อส่งอีเมลยืนยันไปยัง: {email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "การแมปประเภทธุรกรรมไม่ถูกต้อง",
"transaction time format invalid": "รูปแบบเวลาธุรกรรมไม่ถูกต้อง",
"transaction time zone format invalid": "รูปแบบโซนเวลาธุรกรรมไม่ถูกต้อง",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "รหัสหมวดหมู่ธุรกรรมไม่ถูกต้อง",
"transaction category not found": "ไม่พบหมวดหมู่ธุรกรรม",
"transaction category type is invalid": "ประเภทหมวดหมู่ธุรกรรมไม่ถูกต้อง",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "คุณแน่ใจหรือว่าต้องการลบบัญชีย่อยนี้?",
"Unable to delete this account": "ไม่สามารถลบบัญชีนี้ได้",
"Unable to delete this sub-account": "ไม่สามารถลบบัญชีย่อยนี้ได้",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "งบกระทบยอด",
"Account Balance Trends": "แนวโน้มยอดบัญชี",
"Update Closing Balance": "อัปเดตยอดปิดบัญชี",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "หมวดโอนเริ่มต้น",
"Invalid Account": "บัญชีไม่ถูกต้อง",
"Target Account": "บัญชีเป้าหมาย",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "แท็กไม่ถูกต้อง",
"Target Tag": "แท็กเป้าหมาย",
"Remove Tag": "ลบแท็ก",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "Ви впевнені, що хочете імпортувати {count} транзакцій?",
"importingTransactions": "Importing ({process}%)",
"importTransactionResult": "Ви успішно імпортували {count} транзакцій.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"accountActivationAndResendValidationEmailTip": "Посилання для активації облікового запису було надіслано на вашу електронну адресу: {email}. Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно.",
"resendValidationEmailTip": "Якщо ви не отримали лист, введіть пароль ще раз і натисніть кнопку нижче, щоб надіслати лист повторно на адресу: {email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Некоректне зіставлення типу транзакції",
"transaction time format invalid": "Неправильний формат часу транзакції",
"transaction time zone format invalid": "Неправильний формат часового поясу транзакції",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "ID категорії транзакції недійсний",
"transaction category not found": "Категорію транзакції не знайдено",
"transaction category type is invalid": "Тип категорії транзакції недійсний",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Ви впевнені, що хочете видалити цей субрахунок?",
"Unable to delete this account": "Не вдалося видалити цей рахунок",
"Unable to delete this sub-account": "Не вдалося видалити цей субрахунок",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "Категорія переказів за замовчуванням",
"Invalid Account": "Недійсний рахунок",
"Target Account": "Цільовий рахунок",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Недійсний тег",
"Target Tag": "Цільовий тег",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"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.",
"moveTransactionsInAccountTip": "You CANNOT undo this action. This will move all transactions from {fromAccount} to {toAccount}.",
"clearTransactionsInAccountTip": "You CANNOT undo this action. This will clear your transactions data in {account}. Please enter your current password to confirm.",
"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}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "Transaction type mapping is invalid",
"transaction time format invalid": "Transaction time format is invalid",
"transaction time zone format invalid": "Transaction time zone format is invalid",
"cannot move transaction to same account": "Cannot move transaction to the same account",
"cannot move transaction from or to hidden account": "Cannot move transaction from or to hidden account",
"cannot move transaction from or to parent account": "Cannot move transaction from or to parent account",
"cannot move transaction between accounts with different currencies": "Cannot move transaction between accounts with different currencies",
"transaction category id is invalid": "ID danh mục giao dịch không hợp lệ",
"transaction category not found": "Không tìm thấy danh mục giao dịch",
"transaction category type is invalid": "Loại danh mục giao dịch không hợp lệ",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
"Unable to delete this account": "Không thể xóa tài khoản này",
"Unable to delete this sub-account": "Unable to delete this sub-account",
"Move All Transactions": "Move All Transactions",
"Are you sure you want to move all transactions?": "Are you sure you want to move all transactions?",
"Unable to move transactions": "Unable to move transactions",
"All transactions in this account has been moved.": "All transactions in this account has been moved.",
"Reconciliation Statement": "Reconciliation Statement",
"Account Balance Trends": "Account Balance Trends",
"Update Closing Balance": "Update Closing Balance",
@@ -1886,6 +1895,8 @@
"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",
"Confirm Target Account Name": "Confirm Target Account Name",
"Please re-enter the target account name to confirm": "Please re-enter the target account name to confirm",
"Invalid Tag": "Thẻ không hợp lệ",
"Target Tag": "Thẻ mục tiêu",
"Remove Tag": "Remove Tag",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "您确定要导入 {count} 个交易?",
"importingTransactions": "正在导入 ({process}%)",
"importTransactionResult": "您已经成功导入 {count} 个交易。",
"moveTransactionsInAccountTip": "您不能撤销该操作。该操作将会把 {fromAccount} 账户中所有的交易数据移动到 {toAccount}。",
"clearTransactionsInAccountTip": "您不能撤销该操作。该操作将会清除您在 {account} 账户中的交易数据。请输入您当前的密码以确认。",
"accountActivationAndResendValidationEmailTip": "账号激活链接已经发送到您的邮箱地址:{email},如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件。",
"resendValidationEmailTip": "如果您没有收到邮件,请再次输入密码并点击下方的按钮重新发送验证邮件到:{email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "交易类型映射无效",
"transaction time format invalid": "交易时间格式无效",
"transaction time zone format invalid": "交易时区格式无效",
"cannot move transaction to same account": "不能将交易移动到相同的账户",
"cannot move transaction from or to hidden account": "不能从隐藏账户移动交易或移动交易到隐藏账户",
"cannot move transaction from or to parent account": "不能从父账户移动交易或移动交易到父账户",
"cannot move transaction between accounts with different currencies": "不能在不同货币的账户之间移动交易",
"transaction category id is invalid": "交易分类ID无效",
"transaction category not found": "交易分类不存在",
"transaction category type is invalid": "交易分类类型无效",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "您确定要删除该子账户?",
"Unable to delete this account": "无法删除该账户",
"Unable to delete this sub-account": "无法删除该子账户",
"Move All Transactions": "移动所有交易",
"Are you sure you want to move all transactions?": "您确定要移动所有交易?",
"Unable to move transactions": "无法移动交易",
"All transactions in this account has been moved.": "该账户中的所有交易已被移动。",
"Reconciliation Statement": "对账单",
"Account Balance Trends": "账户余额趋势",
"Update Closing Balance": "更新期末余额",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "默认转账分类",
"Invalid Account": "无效账户",
"Target Account": "目标账户",
"Confirm Target Account Name": "确认目标账户名称",
"Please re-enter the target account name to confirm": "请重新输入目标账户名称以确认",
"Invalid Tag": "无效标签",
"Target Tag": "目标标签",
"Remove Tag": "删除标签",
+11
View File
@@ -126,6 +126,7 @@
"confirmImportTransactions": "您確定要匯入 {count} 個交易?",
"importingTransactions": "正在匯入 ({process}%)",
"importTransactionResult": "您已經成功匯入 {count} 個交易。",
"moveTransactionsInAccountTip": "您不能還原此操作。此操作將會把 {fromAccount} 帳戶中的所有交易資料移動到 {toAccount}。",
"clearTransactionsInAccountTip": "您不能還原此操作。此操作將會清除您在 {account} 帳戶中的交易資料。請輸入您目前的密碼以確認。",
"accountActivationAndResendValidationEmailTip": "帳號啟用連結已經傳送到您的信箱地址:{email},如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件。",
"resendValidationEmailTip": "如果您沒有收到郵件,請再次輸入密碼並點擊下方的按鈕重新發送驗證郵件到:{email}"
@@ -1163,6 +1164,10 @@
"transaction type mapping invalid": "交易類型對應無效",
"transaction time format invalid": "交易時間格式無效",
"transaction time zone format invalid": "交易時區格式無效",
"cannot move transaction to same account": "不能將交易移動到相同的帳戶",
"cannot move transaction from or to hidden account": "不能從隱藏帳戶移動交易或移動交易到隱藏帳戶",
"cannot move transaction from or to parent account": "不能從父帳戶移動交易或移動交易到父帳戶",
"cannot move transaction between accounts with different currencies": "不能在不同幣別的帳戶之間移動交易",
"transaction category id is invalid": "交易分類ID無效",
"transaction category not found": "交易分類不存在",
"transaction category type is invalid": "交易分類類型無效",
@@ -1704,6 +1709,10 @@
"Are you sure you want to delete this sub-account?": "您確定要刪除這個子帳戶?",
"Unable to delete this account": "無法刪除此帳戶",
"Unable to delete this sub-account": "無法刪除此子帳戶",
"Move All Transactions": "移動所有交易",
"Are you sure you want to move all transactions?": "您確定要移動所有交易?",
"Unable to move transactions": "無法移動交易",
"All transactions in this account has been moved.": "此帳戶中的所有交易已被移動。",
"Reconciliation Statement": "對帳單",
"Account Balance Trends": "帳戶餘額趨勢",
"Update Closing Balance": "更新期末餘額",
@@ -1886,6 +1895,8 @@
"Default Transfer Category": "預設轉帳分類",
"Invalid Account": "無效帳戶",
"Target Account": "目標帳戶",
"Confirm Target Account Name": "確認目標帳戶名稱",
"Please re-enter the target account name to confirm": "請再次輸入目標帳戶名稱以確認",
"Invalid Tag": "無效標籤",
"Target Tag": "目標標籤",
"Remove Tag": "移除標籤",
+5
View File
@@ -488,6 +488,11 @@ export interface TransactionModifyRequest {
readonly geoLocation?: TransactionGeoLocationRequest;
}
export interface TransactionMoveBetweenAccountsRequest {
readonly fromAccountId: string;
readonly toAccountId: string;
}
export interface TransactionDeleteRequest {
readonly id: string;
}
+6
View File
@@ -14,6 +14,7 @@ import TransactionAmountFilterPage from '@/views/mobile/transactions/AmountFilte
import AccountListPage from '@/views/mobile/accounts/ListPage.vue';
import AccountEditPage from '@/views/mobile/accounts/EditPage.vue';
import AccountReconciliationStatementPage from '@/views/mobile/accounts/ReconciliationStatementPage.vue';
import AccountMoveAllTransactionsPage from '@/views/mobile/accounts/MoveAllTransactionsPage.vue';
import StatisticsTransactionPage from '@/views/mobile/statistics/TransactionPage.vue';
import StatisticsSettingsPage from '@/views/mobile/statistics/SettingsPage.vue';
@@ -197,6 +198,11 @@ const routes: Router.RouteParameters[] = [
async: asyncResolve(AccountReconciliationStatementPage),
beforeEnter: [checkLogin]
},
{
path: '/account/move_all_transactions',
async: asyncResolve(AccountMoveAllTransactionsPage),
beforeEnter: [checkLogin]
},
{
path: '/statistic/transaction',
async: asyncResolve(StatisticsTransactionPage),
+46
View File
@@ -1103,6 +1103,51 @@ export const useTransactionsStore = defineStore('transactions', () => {
});
}
function moveAllTransactionsBetweenAccounts({ fromAccountId, toAccountId }: { fromAccountId: string, toAccountId: string }): Promise<boolean> {
return new Promise((resolve, reject) => {
services.moveAllTransactionsBetweenAccounts({ fromAccountId, toAccountId }).then(response => {
const data = response.data;
if (!data || !data.success || !data.result) {
reject({ message: 'Unable to move transactions' });
return;
}
if (!transactionListStateInvalid.value) {
updateTransactionListInvalidState(true);
}
if (!transactionReconciliationStatementStateInvalid.value) {
updateTransactionReconciliationStatementInvalidState(true);
}
if (!accountsStore.accountListStateInvalid) {
accountsStore.updateAccountListInvalidState(true);
}
if (!overviewStore.transactionOverviewStateInvalid) {
overviewStore.updateTransactionOverviewInvalidState(true);
}
if (!statisticsStore.transactionStatisticsStateInvalid) {
statisticsStore.updateTransactionStatisticsInvalidState(true);
}
resolve(data.result);
}).catch(error => {
logger.error('failed to move transactions', error);
if (error.response && error.response.data && error.response.data.errorMessage) {
reject({ error: error.response.data });
} else if (!error.processed) {
reject({ message: 'Unable to move transactions' });
} else {
reject(error);
}
});
});
}
function deleteTransaction({ transaction, defaultCurrency, beforeResolve }: { transaction: TransactionInfoResponse, defaultCurrency: string, beforeResolve?: BeforeResolveFunction }): Promise<boolean> {
return new Promise((resolve, reject) => {
services.deleteTransaction({
@@ -1405,6 +1450,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
getReconciliationStatements,
getTransaction,
saveTransaction,
moveAllTransactionsBetweenAccounts,
deleteTransaction,
recognizeReceiptImage,
cancelRecognizeReceiptImage,
@@ -0,0 +1,61 @@
import { ref, computed } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { useSettingsStore } from '@/stores/setting.ts';
import { useAccountsStore } from '@/stores/account.ts';
import { Account, type CategorizedAccountWithDisplayBalance } from '@/models/account.ts';
export function useMoveAllTransactionsPageBase() {
const { tt, getCategorizedAccountsWithDisplayBalance } = useI18n();
const settingsStore = useSettingsStore();
const accountsStore = useAccountsStore();
const moving = ref<boolean>(false);
const fromAccount = ref<Account | undefined>(undefined);
const toAccountId = ref<string>('');
const toAccountName = ref<string>('');
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
const allAccounts = computed<Account[]>(() => accountsStore.allPlainAccounts);
const allVisibleAccounts = computed<Account[]>(() => accountsStore.allVisiblePlainAccounts);
const allVisibleCategorizedAccounts = computed<CategorizedAccountWithDisplayBalance[]>(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value));
const displayToAccountName = computed<string>(() => {
if (!toAccountId.value) {
return tt('Target Account');
}
return Account.findAccountNameById(allAccounts.value, toAccountId.value, tt('Target Account')) || tt('Target Account');
});
const isToAccountNameValid = computed<boolean>(() => {
if (!toAccountId.value || !toAccountName.value) {
return false;
}
const expectedAccountName = Account.findAccountNameById(allAccounts.value, toAccountId.value);
if (!expectedAccountName) {
return false;
}
return expectedAccountName === toAccountName.value;
});
return {
// states
moving,
fromAccount,
toAccountId,
toAccountName,
// computed states
allAccounts,
allVisibleAccounts,
allVisibleCategorizedAccounts,
displayToAccountName,
isToAccountNameValid
};
}
+31 -4
View File
@@ -250,10 +250,21 @@
</v-btn>
<v-btn class="px-2 ms-1" density="comfortable" color="default" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }"
:disabled="loading" :prepend-icon="mdiEraser"
v-if="element.type === AccountType.SingleAccount.type || element.getSubAccount(activeSubAccount[element.id])"
@click="clearAllTransactions(element)">
{{ tt('Clear All Transactions') }}
:disabled="loading" :prepend-icon="mdiDotsHorizontalCircleOutline"
v-if="element.type === AccountType.SingleAccount.type || element.getSubAccount(activeSubAccount[element.id])">
{{ tt('More') }}
<v-menu activator="parent" :open-on-hover="true">
<v-list>
<v-list-item class="text-sm" density="compact"
:title="tt('Move All Transactions')"
:prepend-icon="mdiSwapHorizontal"
@click="moveAllTransactions(element)"></v-list-item>
<v-list-item class="text-sm" density="compact"
:title="tt('Clear All Transactions')"
:prepend-icon="mdiEraser"
@click="clearAllTransactions(element)"></v-list-item>
</v-list>
</v-menu>
</v-btn>
<v-btn class="px-2 ms-1" density="comfortable" color="default" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }"
@@ -289,6 +300,7 @@
<edit-dialog ref="editDialog" />
<reconciliation-statement-dialog ref="reconciliationStatementDialog"
@error="onShowDateRangeError" />
<move-all-transactions-dialog ref="moveAllTransactionsDialog" />
<clear-all-transactions-dialog ref="clearAllTransactionsDialog" />
<date-range-selection-dialog :title="tt('Custom Date Range')"
@@ -305,6 +317,7 @@ import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
import SnackBar from '@/components/desktop/SnackBar.vue';
import EditDialog from './list/dialogs/EditDialog.vue';
import ReconciliationStatementDialog from './list/dialogs/ReconciliationStatementDialog.vue';
import MoveAllTransactionsDialog from '@/views/desktop/accounts/list/dialogs/MoveAllTransactionsDialog.vue';
import ClearAllTransactionsDialog from '@/views/desktop/accounts/list/dialogs/ClearAllTransactionsDialog.vue';
import AccountFilterSettingsCard from '@/views/desktop/common/cards/AccountFilterSettingsCard.vue';
@@ -331,6 +344,8 @@ import {
mdiSquareRounded,
mdiMenu,
mdiPencilOutline,
mdiDotsHorizontalCircleOutline,
mdiSwapHorizontal,
mdiEraser,
mdiDeleteOutline,
mdiListBoxOutline,
@@ -343,6 +358,7 @@ type ConfirmDialogType = InstanceType<typeof ConfirmDialog>;
type SnackBarType = InstanceType<typeof SnackBar>;
type EditDialogType = InstanceType<typeof EditDialog>;
type ReconciliationStatementDialogType = InstanceType<typeof ReconciliationStatementDialog>;
type MoveAllTransactionsDialogType = InstanceType<typeof MoveAllTransactionsDialog>;
type ClearAllTransactionsDialogType = InstanceType<typeof ClearAllTransactionsDialog>;
const display = useDisplay();
@@ -372,6 +388,7 @@ const confirmDialog = useTemplateRef<ConfirmDialogType>('confirmDialog');
const snackbar = useTemplateRef<SnackBarType>('snackbar');
const editDialog = useTemplateRef<EditDialogType>('editDialog');
const reconciliationStatementDialog = useTemplateRef<ReconciliationStatementDialogType>('reconciliationStatementDialog');
const moveAllTransactionsDialog = useTemplateRef<MoveAllTransactionsDialogType>('moveAllTransactionsDialog');
const clearAllTransactionsDialog = useTemplateRef<ClearAllTransactionsDialogType>('clearAllTransactionsDialog');
const activeAccountCategoryType = ref<number>(AccountCategory.Default.type);
@@ -525,6 +542,16 @@ function showReconciliationStatementCustomDateRangeDialog(account: Account, date
});
}
function moveAllTransactions(account: Account): void {
moveAllTransactionsDialog.value?.open(account).then(() => {
snackbar.value?.showMessage('All transactions in this account has been moved.');
if (accountsStore.accountListStateInvalid && !loading.value) {
reload(false);
}
});
}
function clearAllTransactions(account: Account): void {
clearAllTransactionsDialog.value?.open(account).then(() => {
snackbar.value?.showMessage('All transactions in this account has been cleared');
@@ -0,0 +1,160 @@
<template>
<v-dialog width="640" :persistent="true" v-model="showState">
<v-card class="pa-2 pa-sm-4 pa-md-4">
<template #title>
<div class="d-flex align-center justify-center">
<h4 class="text-h4 text-wrap">{{ tt('Are you sure you want to move all transactions?') }}</h4>
</div>
</template>
<v-card-text>{{ tt('format.misc.moveTransactionsInAccountTip', { fromAccount: fromAccount?.name, toAccount: displayToAccountName }) }}</v-card-text>
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
<v-row>
<v-col cols="12" md="12">
<two-column-select primary-key-field="id" primary-value-field="category"
primary-title-field="name" primary-footer-field="displayBalance"
primary-icon-field="icon" primary-icon-type="account"
primary-sub-items-field="accounts"
:primary-title-i18n="true"
secondary-key-field="id" secondary-value-field="id"
secondary-title-field="name" secondary-footer-field="displayBalance"
secondary-icon-field="icon" secondary-icon-type="account" secondary-color-field="color"
:disabled="loading || moving || !allVisibleAccounts.length"
:enable-filter="true" :filter-placeholder="tt('Find account')" :filter-no-items-text="tt('No available account')"
:label="tt('Target Account')"
:placeholder="tt('Target Account')"
:items="allVisibleCategorizedAccounts"
:no-item-text="Account.findAccountNameById(allAccounts, toAccountId, tt('Unspecified'))"
v-model="toAccountId">
</two-column-select>
</v-col>
<v-col cols="12" md="12">
<v-text-field type="text"
persistent-placeholder
:disabled="moving"
:label="tt('Confirm Target Account Name')"
:placeholder="tt('Please re-enter the target account name to confirm')"
v-model="toAccountName"
/>
</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="!fromAccount || !toAccountId || fromAccount?.id === toAccountId || !toAccountName || !isToAccountNameValid || moving" @click="confirm">
{{ tt('Confirm') }}
<v-progress-circular indeterminate size="22" class="ms-2" v-if="moving"></v-progress-circular>
</v-btn>
<v-btn color="secondary" variant="tonal" :disabled="moving" @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 { useMoveAllTransactionsPageBase } from '@/views/base/accounts/MoveAllTransactionsPageBase.ts'
import { useAccountsStore } from '@/stores/account.ts';
import { useTransactionsStore } from '@/stores/transaction.ts';
import { Account } from '@/models/account.ts';
type SnackBarType = InstanceType<typeof SnackBar>;
const { tt } = useI18n();
const {
moving,
fromAccount,
toAccountId,
toAccountName,
allAccounts,
allVisibleAccounts,
allVisibleCategorizedAccounts,
displayToAccountName,
isToAccountNameValid
} = useMoveAllTransactionsPageBase();
const accountsStore = useAccountsStore();
const transactionsStore = useTransactionsStore();
let resolveFunc: (() => void) | null = null;
let rejectFunc: ((reason?: unknown) => void) | null = null;
const snackbar = useTemplateRef<SnackBarType>('snackbar');
const showState = ref<boolean>(false);
const loading = ref<boolean>(false);
function init(): void {
accountsStore.loadAllAccounts({
force: false
}).then(() => {
loading.value = false;
}).catch(error => {
loading.value = false;
if (!error.processed) {
snackbar.value?.showError(error);
}
});
}
function open(account: Account): Promise<void> {
showState.value = true;
moving.value = false;
fromAccount.value = account;
toAccountId.value = '';
toAccountName.value = '';
return new Promise((resolve, reject) => {
resolveFunc = resolve;
rejectFunc = reject;
});
}
function confirm(): void {
if (!fromAccount.value || !toAccountId.value || fromAccount.value?.id === toAccountId.value || !toAccountName.value || !isToAccountNameValid.value) {
return;
}
moving.value = true;
transactionsStore.moveAllTransactionsBetweenAccounts({
fromAccountId: fromAccount.value.id,
toAccountId: toAccountId.value
}).then(() => {
moving.value = false;
resolveFunc?.();
showState.value = false;
}).catch(error => {
moving.value = false;
if (!error.processed) {
snackbar.value?.showError(error);
}
});
}
function cancel(): void {
rejectFunc?.();
showState.value = false;
}
defineExpose({
open
});
init();
</script>
+13
View File
@@ -163,6 +163,7 @@
<f7-actions-button @click="showReconciliationStatement(accountForMoreActionSheet)">{{ tt('Reconciliation Statement') }}</f7-actions-button>
</f7-actions-group>
<f7-actions-group v-if="accountForMoreActionSheet && accountForMoreActionSheet.type === AccountType.SingleAccount.type">
<f7-actions-button @click="moveAllTransactions(accountForMoreActionSheet)">{{ tt('Move All Transactions') }}</f7-actions-button>
<f7-actions-button color="red" @click="showPasswordSheetForClearAllTransaction(accountForMoreActionSheet)">{{ tt('Clear All Transactions') }}</f7-actions-button>
</f7-actions-group>
<template v-if="accountForMoreActionSheet && accountForMoreActionSheet.type === AccountType.MultiSubAccounts.type">
@@ -171,6 +172,7 @@
v-show="showHidden || !subAccount.hidden">
<f7-actions-label>{{ subAccount.name }}</f7-actions-label>
<f7-actions-button @click="showReconciliationStatement(subAccount)">{{ tt('Reconciliation Statement') }}</f7-actions-button>
<f7-actions-button @click="moveAllTransactions(subAccount)">{{ tt('Move All Transactions') }}</f7-actions-button>
<f7-actions-button color="red" @click="showPasswordSheetForClearAllTransaction(subAccount)">{{ tt('Clear All Transactions') }}</f7-actions-button>
</f7-actions-group>
</template>
@@ -363,6 +365,17 @@ function showReconciliationStatement(account: Account | null): void {
accountForMoreActionSheet.value = null;
}
function moveAllTransactions(account: Account | null): void {
if (!account) {
showAlert('An error occurred');
return;
}
props.f7router.navigate('/account/move_all_transactions?fromAccountId=' + account.id);
showAccountMoreActionSheet.value = false;
accountForMoreActionSheet.value = null;
}
function showPasswordSheetForClearAllTransaction(account: Account | null): void {
if (!account) {
showAlert('An error occurred');
@@ -0,0 +1,165 @@
<template>
<f7-page @page:afterin="onPageAfterIn">
<f7-navbar>
<f7-nav-left :back-link="tt('Back')"></f7-nav-left>
<f7-nav-title :title="tt('Move All Transactions')"></f7-nav-title>
<f7-nav-right>
<f7-link :class="{ 'disabled': !fromAccount || !toAccountId || fromAccount?.id === toAccountId || !toAccountName || !isToAccountNameValid || moving }" :text="tt('Confirm')" @click="confirm"></f7-link>
</f7-nav-right>
</f7-navbar>
<f7-card
:title="tt('Are you sure you want to move all transactions?')"
:content="tt('format.misc.moveTransactionsInAccountTip', { fromAccount: fromAccount?.name, toAccount: displayToAccountName })">
</f7-card>
<f7-list strong inset dividers class="margin-vertical skeleton-text" v-if="loading">
<f7-list-item class="list-item-with-header-and-title list-item-no-item-after" header="Target Account" title="Unspecified"></f7-list-item>
<f7-list-item class="list-item-with-header-and-title list-item-no-item-after" header="Confirm Target Account Name" title="Unspecified"></f7-list-item>
</f7-list>
<f7-list form strong inset dividers class="margin-vertical" v-if="!loading">
<f7-list-item
class="list-item-with-header-and-title"
link="#" no-chevron
:class="{ 'disabled': moving || !allVisibleAccounts.length }"
:header="tt('Target Account')"
:title="Account.findAccountNameById(allAccounts, toAccountId, tt('Unspecified'))"
@click="showAccountSheet = true"
>
<two-column-list-item-selection-sheet primary-key-field="id" primary-value-field="category"
primary-title-field="name" primary-footer-field="displayBalance"
primary-icon-field="icon" primary-icon-type="account"
primary-sub-items-field="accounts"
:primary-title-i18n="true"
secondary-key-field="id" secondary-value-field="id"
secondary-title-field="name" secondary-footer-field="displayBalance"
secondary-icon-field="icon" secondary-icon-type="account" secondary-color-field="color"
:enable-filter="true" :filter-placeholder="tt('Find account')" :filter-no-items-text="tt('No available account')"
:items="allVisibleCategorizedAccounts"
v-model:show="showAccountSheet"
v-model="toAccountId">
</two-column-list-item-selection-sheet>
</f7-list-item>
<f7-list-input
type="text"
clear-button
:label="tt('Confirm Target Account Name')"
:placeholder="tt('Please re-enter the target account name to confirm')"
v-model:value="toAccountName"
></f7-list-input>
</f7-list>
</f7-page>
</template>
<script setup lang="ts">
import { ref} from 'vue';
import type { Router } from 'framework7/types';
import { useI18n } from '@/locales/helpers.ts';
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
import { useMoveAllTransactionsPageBase } from '@/views/base/accounts/MoveAllTransactionsPageBase.ts'
import { useAccountsStore } from '@/stores/account.ts';
import { useTransactionsStore } from '@/stores/transaction.ts';
import { Account } from '@/models/account.ts';
const props = defineProps<{
f7route: Router.Route;
f7router: Router.Router;
}>();
const { tt } = useI18n();
const { showToast, routeBackOnError } = useI18nUIComponents();
const {
moving,
fromAccount,
toAccountId,
toAccountName,
allAccounts,
allVisibleAccounts,
allVisibleCategorizedAccounts,
displayToAccountName,
isToAccountNameValid
} = useMoveAllTransactionsPageBase();
const accountsStore = useAccountsStore();
const transactionsStore = useTransactionsStore();
const loading = ref<boolean>(true);
const loadingError = ref<unknown | null>(null);
const showAccountSheet = ref<boolean>(false);
function init(): void {
const query = props.f7route.query;
const fromAccountId = query['fromAccountId'];
if (!fromAccountId) {
showToast('Parameter Invalid');
loadingError.value = 'Parameter Invalid';
return;
}
accountsStore.loadAllAccounts({
force: false
}).then(() => {
loading.value = false;
const account = accountsStore.allAccountsMap[fromAccountId];
if (!account) {
showToast('Parameter Invalid');
loadingError.value = 'Parameter Invalid';
return;
}
fromAccount.value = account;
}).catch(error => {
if (error.processed) {
loading.value = false;
} else {
loadingError.value = error;
showToast(error.message || error);
}
});
}
function confirm(): void {
const router = props.f7router;
if (!fromAccount.value || !toAccountId.value || fromAccount.value?.id === toAccountId.value || !toAccountName.value || !isToAccountNameValid.value) {
return;
}
moving.value = true;
showLoading(() => moving.value);
transactionsStore.moveAllTransactionsBetweenAccounts({
fromAccountId: fromAccount.value.id,
toAccountId: toAccountId.value
}).then(() => {
moving.value = false;
hideLoading();
showToast('All transactions in this account has been moved.');
router.back();
}).catch(error => {
moving.value = false;
hideLoading();
if (!error.processed) {
showToast(error.message || error);
}
});
}
function onPageAfterIn(): void {
routeBackOnError(props.f7router, loadingError);
}
init();
</script>