mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-17 16:24:25 +08:00
support adding / deleting sub account after account created (#77)
This commit is contained in:
+229
-19
@@ -311,11 +311,27 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
if accountModifyReq.Id <= 0 {
|
||||
return nil, errs.ErrAccountIdInvalid
|
||||
}
|
||||
|
||||
utcOffset, err := c.GetClientTimezoneOffset()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] cannot get client timezone offset, because %s", err.Error())
|
||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||
}
|
||||
|
||||
if accountModifyReq.Category < models.ACCOUNT_CATEGORY_CASH || accountModifyReq.Category > models.ACCOUNT_CATEGORY_CERTIFICATE_OF_DEPOSIT {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] account category invalid, category is %d", accountModifyReq.Category)
|
||||
return nil, errs.ErrAccountCategoryInvalid
|
||||
}
|
||||
|
||||
if accountModifyReq.Category != models.ACCOUNT_CATEGORY_CREDIT_CARD && accountModifyReq.CreditCardStatementDate != 0 {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] cannot set statement date with category \"%d\"", accountModifyReq.Category)
|
||||
return nil, errs.ErrCannotSetStatementDateForNonCreditCard
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
accountAndSubAccounts, err := a.accounts.GetAccountAndSubAccountsByAccountId(c, uid, accountModifyReq.Id)
|
||||
|
||||
@@ -331,20 +347,81 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
return nil, errs.ErrAccountNotFound
|
||||
}
|
||||
|
||||
if len(accountModifyReq.SubAccounts)+1 != len(accountAndSubAccounts) {
|
||||
return nil, errs.ErrCannotAddOrDeleteSubAccountsWhenModify
|
||||
if accountModifyReq.Currency != nil && mainAccount.Currency != *accountModifyReq.Currency {
|
||||
return nil, errs.ErrNotSupportedChangeCurrency
|
||||
}
|
||||
|
||||
if accountModifyReq.Category != models.ACCOUNT_CATEGORY_CREDIT_CARD && accountModifyReq.CreditCardStatementDate != 0 {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] cannot set statement date with category \"%d\"", accountModifyReq.Category)
|
||||
return nil, errs.ErrCannotSetStatementDateForNonCreditCard
|
||||
if accountModifyReq.Balance != nil {
|
||||
return nil, errs.ErrNotSupportedChangeBalance
|
||||
}
|
||||
|
||||
if mainAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
|
||||
if accountModifyReq.BalanceTime != nil {
|
||||
return nil, errs.ErrNotSupportedChangeBalanceTime
|
||||
}
|
||||
|
||||
if mainAccount.Type == models.ACCOUNT_TYPE_SINGLE_ACCOUNT {
|
||||
if len(accountModifyReq.SubAccounts) > 0 {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] account cannot have any sub-accounts")
|
||||
return nil, errs.ErrAccountCannotHaveSubAccounts
|
||||
}
|
||||
} else if mainAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
|
||||
if len(accountModifyReq.SubAccounts) < 1 {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] account does not have any sub-accounts")
|
||||
return nil, errs.ErrAccountHaveNoSubAccount
|
||||
}
|
||||
|
||||
for i := 0; i < len(accountModifyReq.SubAccounts); i++ {
|
||||
subAccount := accountModifyReq.SubAccounts[i]
|
||||
subAccountReq := accountModifyReq.SubAccounts[i]
|
||||
|
||||
if subAccount.CreditCardStatementDate != 0 {
|
||||
if subAccountReq.Category != accountModifyReq.Category {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] category of sub-account#%d not equals to parent", i)
|
||||
return nil, errs.ErrSubAccountCategoryNotEqualsToParent
|
||||
}
|
||||
|
||||
if subAccountReq.Id == 0 { // create new sub-account
|
||||
if subAccountReq.Currency == nil {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] sub-account#%d not set currency", i)
|
||||
return nil, errs.ErrAccountCurrencyInvalid
|
||||
} else if subAccountReq.Currency != nil && *subAccountReq.Currency == validators.ParentAccountCurrencyPlaceholder {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] sub-account#%d cannot set currency placeholder", i)
|
||||
return nil, errs.ErrAccountCurrencyInvalid
|
||||
}
|
||||
|
||||
if subAccountReq.Balance == nil {
|
||||
defaultBalance := int64(0)
|
||||
subAccountReq.Balance = &defaultBalance
|
||||
}
|
||||
|
||||
if *subAccountReq.Balance == 0 {
|
||||
defaultBalanceTime := int64(0)
|
||||
subAccountReq.BalanceTime = &defaultBalanceTime
|
||||
}
|
||||
|
||||
if *subAccountReq.Balance != 0 && (subAccountReq.BalanceTime == nil || *subAccountReq.BalanceTime <= 0) {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] sub-account#%d balance time is not set", i)
|
||||
return nil, errs.ErrAccountBalanceTimeNotSet
|
||||
}
|
||||
} else { // modify existed sub-account
|
||||
subAccount, exists := accountMap[subAccountReq.Id]
|
||||
|
||||
if !exists {
|
||||
return nil, errs.ErrAccountNotFound
|
||||
}
|
||||
|
||||
if subAccountReq.Currency != nil && subAccount.Currency != *subAccountReq.Currency {
|
||||
return nil, errs.ErrNotSupportedChangeCurrency
|
||||
}
|
||||
|
||||
if subAccountReq.Balance != nil {
|
||||
return nil, errs.ErrNotSupportedChangeBalance
|
||||
}
|
||||
|
||||
if subAccountReq.BalanceTime != nil {
|
||||
return nil, errs.ErrNotSupportedChangeBalanceTime
|
||||
}
|
||||
}
|
||||
|
||||
if subAccountReq.CreditCardStatementDate != 0 {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] sub-account#%d cannot set statement date", i)
|
||||
return nil, errs.ErrCannotSetStatementDateForSubAccount
|
||||
}
|
||||
@@ -353,6 +430,9 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
|
||||
anythingUpdate := false
|
||||
var toUpdateAccounts []*models.Account
|
||||
var toAddAccounts []*models.Account
|
||||
var toAddAccountBalanceTimes []int64
|
||||
var toDeleteAccountIds []int64
|
||||
|
||||
toUpdateAccount := a.getToUpdateAccount(uid, &accountModifyReq, mainAccount, false)
|
||||
|
||||
@@ -361,18 +441,43 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
toUpdateAccounts = append(toUpdateAccounts, toUpdateAccount)
|
||||
}
|
||||
|
||||
toDeleteAccountIds = a.getToDeleteSubAccountIds(&accountModifyReq, mainAccount, accountAndSubAccounts)
|
||||
|
||||
if len(toDeleteAccountIds) > 0 {
|
||||
anythingUpdate = true
|
||||
}
|
||||
|
||||
maxOrderId := int32(0)
|
||||
|
||||
for i := 0; i < len(accountAndSubAccounts); i++ {
|
||||
account := accountAndSubAccounts[i]
|
||||
|
||||
if account.AccountId != mainAccount.AccountId && account.DisplayOrder > maxOrderId {
|
||||
maxOrderId = account.DisplayOrder
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(accountModifyReq.SubAccounts); i++ {
|
||||
subAccountReq := accountModifyReq.SubAccounts[i]
|
||||
|
||||
if _, exists := accountMap[subAccountReq.Id]; !exists {
|
||||
return nil, errs.ErrAccountNotFound
|
||||
}
|
||||
|
||||
toUpdateSubAccount := a.getToUpdateAccount(uid, subAccountReq, accountMap[subAccountReq.Id], true)
|
||||
|
||||
if toUpdateSubAccount != nil {
|
||||
anythingUpdate = true
|
||||
toUpdateAccounts = append(toUpdateAccounts, toUpdateSubAccount)
|
||||
maxOrderId = maxOrderId + 1
|
||||
newSubAccount := a.createNewSubAccountModelForModify(uid, mainAccount.Type, subAccountReq, maxOrderId)
|
||||
toAddAccounts = append(toAddAccounts, newSubAccount)
|
||||
|
||||
if subAccountReq.BalanceTime != nil {
|
||||
toAddAccountBalanceTimes = append(toAddAccountBalanceTimes, *subAccountReq.BalanceTime)
|
||||
} else {
|
||||
toAddAccountBalanceTimes = append(toAddAccountBalanceTimes, 0)
|
||||
}
|
||||
} else {
|
||||
toUpdateSubAccount := a.getToUpdateAccount(uid, subAccountReq, accountMap[subAccountReq.Id], true)
|
||||
|
||||
if toUpdateSubAccount != nil {
|
||||
anythingUpdate = true
|
||||
toUpdateAccounts = append(toUpdateAccounts, toUpdateSubAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +485,43 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
return nil, errs.ErrNothingWillBeUpdated
|
||||
}
|
||||
|
||||
err = a.accounts.ModifyAccounts(c, uid, toUpdateAccounts)
|
||||
if len(toAddAccounts) > 0 && a.CurrentConfig().EnableDuplicateSubmissionsCheck && accountModifyReq.ClientSessionId != "" {
|
||||
found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_SUBACCOUNT, uid, accountModifyReq.ClientSessionId)
|
||||
|
||||
if found {
|
||||
log.Infof(c, "[accounts.AccountModifyHandler] another account \"id:%s\" modification has been created for user \"uid:%d\"", remark, uid)
|
||||
accountId, err := utils.StringToInt64(remark)
|
||||
|
||||
if err == nil {
|
||||
accountAndSubAccounts, err := a.accounts.GetAccountAndSubAccountsByAccountId(c, uid, accountId)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[accounts.AccountModifyHandler] failed to get existed account \"id:%d\" for user \"uid:%d\", because %s", accountId, uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
accountMap := a.accounts.GetAccountMapByList(accountAndSubAccounts)
|
||||
mainAccount, exists := accountMap[accountId]
|
||||
|
||||
if !exists {
|
||||
return nil, errs.ErrOperationFailed
|
||||
}
|
||||
|
||||
accountInfoResp := mainAccount.ToAccountInfoResponse()
|
||||
|
||||
for i := 0; i < len(accountAndSubAccounts); i++ {
|
||||
if accountAndSubAccounts[i].ParentAccountId == mainAccount.AccountId {
|
||||
subAccountResp := accountAndSubAccounts[i].ToAccountInfoResponse()
|
||||
accountInfoResp.SubAccounts = append(accountInfoResp.SubAccounts, subAccountResp)
|
||||
}
|
||||
}
|
||||
|
||||
return accountInfoResp, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = a.accounts.ModifyAccounts(c, mainAccount, toUpdateAccounts, toAddAccounts, toAddAccountBalanceTimes, toDeleteAccountIds, utcOffset)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[accounts.AccountModifyHandler] failed to update account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
||||
@@ -389,6 +530,10 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
|
||||
log.Infof(c, "[accounts.AccountModifyHandler] user \"uid:%d\" has updated account \"id:%d\" successfully", uid, accountModifyReq.Id)
|
||||
|
||||
if len(toAddAccounts) > 0 {
|
||||
a.SetSubmissionRemarkIfEnable(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_SUBACCOUNT, uid, accountModifyReq.ClientSessionId, utils.Int64ToString(mainAccount.AccountId))
|
||||
}
|
||||
|
||||
accountRespMap := make(map[int64]*models.AccountInfoResponse)
|
||||
|
||||
for i := 0; i < len(toUpdateAccounts); i++ {
|
||||
@@ -405,11 +550,23 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
accountRespMap[accountResp.Id] = accountResp
|
||||
}
|
||||
|
||||
for i := 0; i < len(toAddAccounts); i++ {
|
||||
account := toAddAccounts[i]
|
||||
accountResp := account.ToAccountInfoResponse()
|
||||
accountRespMap[accountResp.Id] = accountResp
|
||||
}
|
||||
|
||||
deletedAccountIds := make(map[int64]bool)
|
||||
|
||||
for i := 0; i < len(toDeleteAccountIds); i++ {
|
||||
deletedAccountIds[toDeleteAccountIds[i]] = true
|
||||
}
|
||||
|
||||
for i := 0; i < len(accountAndSubAccounts); i++ {
|
||||
oldAccount := accountAndSubAccounts[i]
|
||||
_, exists := accountRespMap[oldAccount.AccountId]
|
||||
|
||||
if !exists {
|
||||
if !exists && !deletedAccountIds[oldAccount.AccountId] {
|
||||
oldAccountResp := oldAccount.ToAccountInfoResponse()
|
||||
accountRespMap[oldAccountResp.Id] = oldAccountResp
|
||||
}
|
||||
@@ -418,8 +575,19 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
accountResp := accountRespMap[accountModifyReq.Id]
|
||||
|
||||
for i := 0; i < len(accountAndSubAccounts); i++ {
|
||||
if accountAndSubAccounts[i].ParentAccountId == accountResp.Id {
|
||||
subAccountResp := accountRespMap[accountAndSubAccounts[i].AccountId]
|
||||
account := accountAndSubAccounts[i]
|
||||
|
||||
if account.ParentAccountId == accountResp.Id && !deletedAccountIds[account.AccountId] {
|
||||
subAccountResp := accountRespMap[account.AccountId]
|
||||
accountResp.SubAccounts = append(accountResp.SubAccounts, subAccountResp)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(toAddAccounts); i++ {
|
||||
account := toAddAccounts[i]
|
||||
|
||||
if account.ParentAccountId == accountResp.Id {
|
||||
subAccountResp := accountRespMap[account.AccountId]
|
||||
accountResp.SubAccounts = append(accountResp.SubAccounts, subAccountResp)
|
||||
}
|
||||
}
|
||||
@@ -552,6 +720,24 @@ func (a *AccountsApi) createNewAccountModel(uid int64, accountCreateReq *models.
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AccountsApi) createNewSubAccountModelForModify(uid int64, accountType models.AccountType, accountModifyReq *models.AccountModifyRequest, order int32) *models.Account {
|
||||
accountExtend := &models.AccountExtend{}
|
||||
|
||||
return &models.Account{
|
||||
Uid: uid,
|
||||
Name: accountModifyReq.Name,
|
||||
DisplayOrder: order,
|
||||
Category: accountModifyReq.Category,
|
||||
Type: accountType,
|
||||
Icon: accountModifyReq.Icon,
|
||||
Color: accountModifyReq.Color,
|
||||
Currency: *accountModifyReq.Currency,
|
||||
Balance: *accountModifyReq.Balance,
|
||||
Comment: accountModifyReq.Comment,
|
||||
Extend: accountExtend,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AccountsApi) createSubAccountModels(uid int64, accountCreateReq *models.AccountCreateRequest) ([]*models.Account, []int64) {
|
||||
if len(accountCreateReq.SubAccounts) <= 0 {
|
||||
return nil, nil
|
||||
@@ -609,3 +795,27 @@ func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.Acc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AccountsApi) getToDeleteSubAccountIds(accountModifyReq *models.AccountModifyRequest, mainAccount *models.Account, accountAndSubAccounts []*models.Account) []int64 {
|
||||
newSubAccountIds := make(map[int64]bool, len(accountModifyReq.SubAccounts))
|
||||
|
||||
for i := 0; i < len(accountModifyReq.SubAccounts); i++ {
|
||||
newSubAccountIds[accountModifyReq.SubAccounts[i].Id] = true
|
||||
}
|
||||
|
||||
toDeleteAccountIds := make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(accountAndSubAccounts); i++ {
|
||||
subAccount := accountAndSubAccounts[i]
|
||||
|
||||
if subAccount.AccountId == mainAccount.AccountId {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, exists := newSubAccountIds[subAccount.AccountId]; !exists {
|
||||
toDeleteAccountIds = append(toDeleteAccountIds, subAccount.AccountId)
|
||||
}
|
||||
}
|
||||
|
||||
return toDeleteAccountIds
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ type DuplicateCheckerType uint8
|
||||
const (
|
||||
DUPLICATE_CHECKER_TYPE_BACKGROUND_CRON_JOB DuplicateCheckerType = 0
|
||||
DUPLICATE_CHECKER_TYPE_NEW_ACCOUNT DuplicateCheckerType = 1
|
||||
DUPLICATE_CHECKER_TYPE_NEW_CATEGORY DuplicateCheckerType = 2
|
||||
DUPLICATE_CHECKER_TYPE_NEW_TRANSACTION DuplicateCheckerType = 3
|
||||
DUPLICATE_CHECKER_TYPE_NEW_TEMPLATE DuplicateCheckerType = 4
|
||||
DUPLICATE_CHECKER_TYPE_NEW_PICTURE DuplicateCheckerType = 5
|
||||
DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS DuplicateCheckerType = 6
|
||||
DUPLICATE_CHECKER_TYPE_NEW_SUBACCOUNT DuplicateCheckerType = 2
|
||||
DUPLICATE_CHECKER_TYPE_NEW_CATEGORY DuplicateCheckerType = 3
|
||||
DUPLICATE_CHECKER_TYPE_NEW_TRANSACTION DuplicateCheckerType = 4
|
||||
DUPLICATE_CHECKER_TYPE_NEW_TEMPLATE DuplicateCheckerType = 5
|
||||
DUPLICATE_CHECKER_TYPE_NEW_PICTURE DuplicateCheckerType = 6
|
||||
DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS DuplicateCheckerType = 7
|
||||
DUPLICATE_CHECKER_TYPE_FAILURE_CHECK DuplicateCheckerType = 255
|
||||
)
|
||||
|
||||
+3
-1
@@ -14,7 +14,6 @@ var (
|
||||
ErrParentAccountCannotSetBalance = NewNormalError(NormalSubcategoryAccount, 7, http.StatusBadRequest, "parent account cannot set balance")
|
||||
ErrSubAccountCategoryNotEqualsToParent = NewNormalError(NormalSubcategoryAccount, 8, http.StatusBadRequest, "sub-account category not equals to parent")
|
||||
ErrSubAccountTypeInvalid = NewNormalError(NormalSubcategoryAccount, 9, http.StatusBadRequest, "sub-account type invalid")
|
||||
ErrCannotAddOrDeleteSubAccountsWhenModify = NewNormalError(NormalSubcategoryAccount, 10, http.StatusBadRequest, "cannot add or delete sub-accounts when modify account")
|
||||
ErrSourceAccountNotFound = NewNormalError(NormalSubcategoryAccount, 11, http.StatusBadRequest, "source account not found")
|
||||
ErrDestinationAccountNotFound = NewNormalError(NormalSubcategoryAccount, 12, http.StatusBadRequest, "destination account not found")
|
||||
ErrAccountInUseCannotBeDeleted = NewNormalError(NormalSubcategoryAccount, 13, http.StatusBadRequest, "account is in use and cannot be deleted")
|
||||
@@ -24,4 +23,7 @@ var (
|
||||
ErrCannotSetStatementDateForSubAccount = NewNormalError(NormalSubcategoryAccount, 17, http.StatusBadRequest, "cannot set statement date for sub account")
|
||||
ErrSubAccountNotFound = NewNormalError(NormalSubcategoryAccount, 18, http.StatusBadRequest, "sub-account not found")
|
||||
ErrSubAccountInUseCannotBeDeleted = NewNormalError(NormalSubcategoryAccount, 19, http.StatusBadRequest, "sub-account is in use and cannot be deleted")
|
||||
ErrNotSupportedChangeCurrency = NewNormalError(NormalSubcategoryAccount, 20, http.StatusBadRequest, "not supported to modify account currency")
|
||||
ErrNotSupportedChangeBalance = NewNormalError(NormalSubcategoryAccount, 21, http.StatusBadRequest, "not supported to modify account balance")
|
||||
ErrNotSupportedChangeBalanceTime = NewNormalError(NormalSubcategoryAccount, 22, http.StatusBadRequest, "not supported to modify account balance time")
|
||||
)
|
||||
|
||||
@@ -101,15 +101,19 @@ type AccountCreateRequest struct {
|
||||
|
||||
// AccountModifyRequest represents all parameters of account modification request
|
||||
type AccountModifyRequest struct {
|
||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||
Id int64 `json:"id,string" binding:"required,min=0"`
|
||||
Name string `json:"name" binding:"required,notBlank,max=64"`
|
||||
Category AccountCategory `json:"category" binding:"required"`
|
||||
Icon int64 `json:"icon,string" binding:"min=1"`
|
||||
Color string `json:"color" binding:"required,len=6,validHexRGBColor"`
|
||||
Currency *string `json:"currency" binding:"omitempty,len=3,validCurrency"`
|
||||
Balance *int64 `json:"balance" binding:"omitempty"`
|
||||
BalanceTime *int64 `json:"balanceTime" binding:"omitempty"`
|
||||
Comment string `json:"comment" binding:"max=255"`
|
||||
CreditCardStatementDate int `json:"creditCardStatementDate" binding:"min=0,max=28"`
|
||||
Hidden bool `json:"hidden"`
|
||||
SubAccounts []*AccountModifyRequest `json:"subAccounts" binding:"omitempty"`
|
||||
ClientSessionId string `json:"clientSessionId"`
|
||||
}
|
||||
|
||||
// AccountListRequest represents all parameters of account listing request
|
||||
|
||||
+192
-8
@@ -334,21 +334,81 @@ func (s *AccountService) CreateAccounts(c core.Context, mainAccount *models.Acco
|
||||
}
|
||||
|
||||
// ModifyAccounts saves an existed account model to database
|
||||
func (s *AccountService) ModifyAccounts(c core.Context, uid int64, accounts []*models.Account) error {
|
||||
if uid <= 0 {
|
||||
func (s *AccountService) ModifyAccounts(c core.Context, mainAccount *models.Account, updateAccounts []*models.Account, addSubAccounts []*models.Account, addSubAccountBalanceTimes []int64, removeSubAccountIds []int64, utcOffset int16) error {
|
||||
if mainAccount.Uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
needAccountUuidCount := uint16(len(addSubAccounts))
|
||||
newAccountUuids := s.GenerateUuids(uuid.UUID_TYPE_ACCOUNT, needAccountUuidCount)
|
||||
|
||||
if len(newAccountUuids) < int(needAccountUuidCount) {
|
||||
return errs.ErrSystemIsBusy
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
accounts[i].UpdatedUnixTime = now
|
||||
var addInitTransactions []*models.Transaction
|
||||
|
||||
for i := 0; i < len(updateAccounts); i++ {
|
||||
updateAccounts[i].UpdatedUnixTime = now
|
||||
}
|
||||
|
||||
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
account := accounts[i]
|
||||
updatedRows, err := sess.ID(account.AccountId).Cols("name", "category", "icon", "color", "comment", "extend", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(account)
|
||||
if mainAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
|
||||
defaultTransactionTime := utils.GetMinTransactionTimeFromUnixTime(now)
|
||||
|
||||
for i := 0; i < len(addSubAccounts); i++ {
|
||||
childAccount := addSubAccounts[i]
|
||||
childAccount.AccountId = newAccountUuids[i]
|
||||
childAccount.ParentAccountId = mainAccount.AccountId
|
||||
childAccount.Uid = mainAccount.Uid
|
||||
childAccount.Type = models.ACCOUNT_TYPE_SINGLE_ACCOUNT
|
||||
childAccount.Deleted = false
|
||||
childAccount.CreatedUnixTime = now
|
||||
childAccount.UpdatedUnixTime = now
|
||||
|
||||
if childAccount.Balance != 0 {
|
||||
transactionId := s.GenerateUuid(uuid.UUID_TYPE_TRANSACTION)
|
||||
|
||||
if transactionId < 1 {
|
||||
return errs.ErrSystemIsBusy
|
||||
}
|
||||
|
||||
transactionTime := defaultTransactionTime
|
||||
|
||||
if len(addSubAccountBalanceTimes) > i && addSubAccountBalanceTimes[i] > 0 {
|
||||
transactionTime = utils.GetMinTransactionTimeFromUnixTime(addSubAccountBalanceTimes[i])
|
||||
} else {
|
||||
defaultTransactionTime++
|
||||
}
|
||||
|
||||
newTransaction := &models.Transaction{
|
||||
TransactionId: transactionId,
|
||||
Uid: childAccount.Uid,
|
||||
Deleted: false,
|
||||
Type: models.TRANSACTION_DB_TYPE_MODIFY_BALANCE,
|
||||
TransactionTime: transactionTime,
|
||||
TimezoneUtcOffset: utcOffset,
|
||||
AccountId: childAccount.AccountId,
|
||||
Amount: childAccount.Balance,
|
||||
RelatedAccountId: childAccount.AccountId,
|
||||
RelatedAccountAmount: childAccount.Balance,
|
||||
CreatedUnixTime: now,
|
||||
UpdatedUnixTime: now,
|
||||
}
|
||||
|
||||
addInitTransactions = append(addInitTransactions, newTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userDataDb := s.UserDataDB(mainAccount.Uid)
|
||||
|
||||
return userDataDb.DoTransaction(c, func(sess *xorm.Session) error {
|
||||
// update accounts
|
||||
for i := 0; i < len(updateAccounts); i++ {
|
||||
account := updateAccounts[i]
|
||||
updatedRows, err := sess.ID(account.AccountId).Cols("name", "category", "icon", "color", "comment", "extend", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", account.Uid, false).Update(account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -357,6 +417,130 @@ func (s *AccountService) ModifyAccounts(c core.Context, uid int64, accounts []*m
|
||||
}
|
||||
}
|
||||
|
||||
// add new sub accounts
|
||||
for i := 0; i < len(addSubAccounts); i++ {
|
||||
account := addSubAccounts[i]
|
||||
_, err := sess.Insert(account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add init transaction for new sub accounts
|
||||
for i := 0; i < len(addInitTransactions); i++ {
|
||||
transaction := addInitTransactions[i]
|
||||
|
||||
insertTransactionSavePointName := "insert_transaction"
|
||||
err := userDataDb.SetSavePoint(sess, insertTransactionSavePointName)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[accounts.ModifyAccounts] failed to set save point \"%s\", because %s", insertTransactionSavePointName, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
createdRows, err := sess.Insert(transaction)
|
||||
|
||||
if err != nil || createdRows < 1 { // maybe another transaction has same time
|
||||
err = userDataDb.RollbackToSavePoint(sess, insertTransactionSavePointName)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[accounts.ModifyAccounts] failed to rollback to save point \"%s\", because %s", insertTransactionSavePointName, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
sameSecondLatestTransaction := &models.Transaction{}
|
||||
minTransactionTime := utils.GetMinTransactionTimeFromUnixTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime))
|
||||
maxTransactionTime := utils.GetMaxTransactionTimeFromUnixTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime))
|
||||
|
||||
has, err := sess.Where("uid=? AND transaction_time>=? AND transaction_time<=?", transaction.Uid, minTransactionTime, maxTransactionTime).OrderBy("transaction_time desc").Limit(1).Get(sameSecondLatestTransaction)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return errs.ErrDatabaseOperationFailed
|
||||
} else if sameSecondLatestTransaction.TransactionTime == maxTransactionTime-1 {
|
||||
return errs.ErrTooMuchTransactionInOneSecond
|
||||
}
|
||||
|
||||
transaction.TransactionTime = sameSecondLatestTransaction.TransactionTime + 1
|
||||
createdRows, err := sess.Insert(transaction)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if createdRows < 1 {
|
||||
return errs.ErrDatabaseOperationFailed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove sub accounts
|
||||
if len(removeSubAccountIds) > 0 {
|
||||
subAccountsCount, err := sess.Where("uid=? AND deleted=? AND parent_account_id=?", mainAccount.Uid, false, mainAccount.AccountId).Count(&models.Account{})
|
||||
|
||||
if subAccountsCount <= int64(len(removeSubAccountIds)) {
|
||||
return errs.ErrAccountHaveNoSubAccount
|
||||
}
|
||||
|
||||
var relatedTransactionsByAccount []*models.Transaction
|
||||
err = sess.Cols("transaction_id", "uid", "deleted", "account_id", "type").Where("uid=? AND deleted=?", mainAccount.Uid, false).In("account_id", removeSubAccountIds).Limit(len(removeSubAccountIds) + 1).Find(&relatedTransactionsByAccount)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(relatedTransactionsByAccount) > len(removeSubAccountIds) {
|
||||
return errs.ErrSubAccountInUseCannotBeDeleted
|
||||
} else if len(relatedTransactionsByAccount) > 0 {
|
||||
accountTransactionExists := make(map[int64]bool)
|
||||
|
||||
for i := 0; i < len(relatedTransactionsByAccount); i++ {
|
||||
transaction := relatedTransactionsByAccount[i]
|
||||
|
||||
if transaction.Type != models.TRANSACTION_DB_TYPE_MODIFY_BALANCE {
|
||||
return errs.ErrAccountInUseCannotBeDeleted
|
||||
} else if _, exists := accountTransactionExists[transaction.AccountId]; exists {
|
||||
return errs.ErrAccountInUseCannotBeDeleted
|
||||
}
|
||||
|
||||
accountTransactionExists[transaction.AccountId] = true
|
||||
}
|
||||
}
|
||||
|
||||
deleteAccountUpdateModel := &models.Account{
|
||||
Balance: 0,
|
||||
Deleted: true,
|
||||
DeletedUnixTime: now,
|
||||
}
|
||||
|
||||
deletedRows, err := sess.Cols("balance", "deleted", "deleted_unix_time").Where("uid=? AND deleted=?", mainAccount.Uid, false).In("account_id", removeSubAccountIds).Update(deleteAccountUpdateModel)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if deletedRows < 1 {
|
||||
return errs.ErrSubAccountNotFound
|
||||
}
|
||||
|
||||
if len(relatedTransactionsByAccount) > 0 {
|
||||
updateTransaction := &models.Transaction{
|
||||
Deleted: true,
|
||||
DeletedUnixTime: now,
|
||||
}
|
||||
|
||||
transactionIds := make([]int64, len(relatedTransactionsByAccount))
|
||||
|
||||
for i := 0; i < len(relatedTransactionsByAccount); i++ {
|
||||
transactionIds[i] = relatedTransactionsByAccount[i].TransactionId
|
||||
}
|
||||
|
||||
deletedTransactionRows, err := sess.Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", mainAccount.Uid, false).In("transaction_id", transactionIds).Update(updateTransaction)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if deletedTransactionRows < int64(len(transactionIds)) {
|
||||
return errs.ErrDatabaseOperationFailed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user