From 01aeb945ff12bcd3aff58711d3c1afbe94ff08b7 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 24 Aug 2025 01:29:26 +0800 Subject: [PATCH] check whether transaction template uses specified accounts / categories / tags when deleting them --- pkg/services/accounts.go | 40 ++++++++++++++++++++++++++ pkg/services/transaction_categories.go | 8 ++++++ pkg/services/transaction_tags.go | 22 ++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/pkg/services/accounts.go b/pkg/services/accounts.go index cb313b2d..0f54b66a 100644 --- a/pkg/services/accounts.go +++ b/pkg/services/accounts.go @@ -1,6 +1,7 @@ package services import ( + "fmt" "strings" "time" @@ -662,9 +663,15 @@ func (s *AccountService) DeleteAccount(c core.Context, uid int64, accountId int6 return errs.ErrAccountNotFound } + var accountAndSubAccountIdsConditions strings.Builder accountAndSubAccountIds := make([]int64, len(accountAndSubAccounts)) for i := 0; i < len(accountAndSubAccounts); i++ { + if accountAndSubAccountIdsConditions.Len() > 0 { + accountAndSubAccountIdsConditions.WriteString(",") + } + + accountAndSubAccountIdsConditions.WriteString("?") accountAndSubAccountIds[i] = accountAndSubAccounts[i].AccountId } @@ -691,6 +698,31 @@ func (s *AccountService) DeleteAccount(c core.Context, uid int64, accountId int6 } } + transactionTemplateQueryCondition := fmt.Sprintf("uid=? AND deleted=? AND (template_type=? || (template_type=? && scheduled_frequency_type<>? && (scheduled_end_time IS NULL OR scheduled_end_time>=?))) AND (account_id IN (%s) OR related_account_id IN (%s))", accountAndSubAccountIdsConditions.String(), accountAndSubAccountIdsConditions.String()) + transactionTemplateQueryConditionParams := make([]any, 0, len(accountAndSubAccountIds)*2+6) + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, uid) + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, false) + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, models.TRANSACTION_TEMPLATE_TYPE_NORMAL) + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, models.TRANSACTION_TEMPLATE_TYPE_SCHEDULE) + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_DISABLED) + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, now) + + for i := 0; i < len(accountAndSubAccountIds); i++ { + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, accountAndSubAccountIds[i]) + } + + for i := 0; i < len(accountAndSubAccountIds); i++ { + transactionTemplateQueryConditionParams = append(transactionTemplateQueryConditionParams, accountAndSubAccountIds[i]) + } + + exists, err := sess.Cols("uid", "deleted", "account_id", "related_account_id", "template_type", "scheduled_frequency_type", "scheduled_end_time").Where(transactionTemplateQueryCondition, transactionTemplateQueryConditionParams...).Limit(1).Exist(&models.TransactionTemplate{}) + + if err != nil { + return err + } else if exists { + return errs.ErrAccountInUseCannotBeDeleted + } + deletedRows, err := sess.Cols("balance", "deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).In("account_id", accountAndSubAccountIds).Update(updateModel) if err != nil { @@ -772,6 +804,14 @@ func (s *AccountService) DeleteSubAccount(c core.Context, uid int64, accountId i } } + exists, err := sess.Cols("uid", "deleted", "account_id", "related_account_id", "template_type", "scheduled_frequency_type", "scheduled_end_time").Where("uid=? AND deleted=? AND (template_type=? || (template_type=? && scheduled_frequency_type<>? && (scheduled_end_time IS NULL OR scheduled_end_time>=?))) AND (account_id=? OR related_account_id=?)", uid, false, models.TRANSACTION_TEMPLATE_TYPE_NORMAL, models.TRANSACTION_TEMPLATE_TYPE_SCHEDULE, models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_DISABLED, now, accountId, accountId).Limit(1).Exist(&models.TransactionTemplate{}) + + if err != nil { + return err + } else if exists { + return errs.ErrSubAccountInUseCannotBeDeleted + } + deletedRows, err := sess.Cols("balance", "deleted", "deleted_unix_time").Where("uid=? AND deleted=? AND account_id=?", uid, false, accountId).Update(updateModel) if err != nil { diff --git a/pkg/services/transaction_categories.go b/pkg/services/transaction_categories.go index 9416d3fd..6ffe51e1 100644 --- a/pkg/services/transaction_categories.go +++ b/pkg/services/transaction_categories.go @@ -397,6 +397,14 @@ func (s *TransactionCategoryService) DeleteCategory(c core.Context, uid int64, c return errs.ErrTransactionCategoryInUseCannotBeDeleted } + exists, err = sess.Cols("uid", "deleted", "category_id", "template_type", "scheduled_frequency_type", "scheduled_end_time").Where("uid=? AND deleted=? AND (template_type=? || (template_type=? && scheduled_frequency_type<>? && (scheduled_end_time IS NULL OR scheduled_end_time>=?)))", uid, false, models.TRANSACTION_TEMPLATE_TYPE_NORMAL, models.TRANSACTION_TEMPLATE_TYPE_SCHEDULE, models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_DISABLED, now).In("category_id", categoryAndSubCategoryIds).Limit(1).Exist(&models.TransactionTemplate{}) + + if err != nil { + return err + } else if exists { + return errs.ErrTransactionCategoryInUseCannotBeDeleted + } + deletedRows, err := sess.Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).In("category_id", categoryAndSubCategoryIds).Update(updateModel) if err != nil { diff --git a/pkg/services/transaction_tags.go b/pkg/services/transaction_tags.go index 30764baa..e7ca28a9 100644 --- a/pkg/services/transaction_tags.go +++ b/pkg/services/transaction_tags.go @@ -396,6 +396,28 @@ func (s *TransactionTagService) DeleteTag(c core.Context, uid int64, tagId int64 return errs.ErrTransactionTagInUseCannotBeDeleted } + var relatedTransactionTemplatesByTag []*models.TransactionTemplate + err = sess.Cols("uid", "deleted", "tag_ids", "template_type", "scheduled_frequency_type", "scheduled_end_time").Where("uid=? AND deleted=? AND (template_type=? || (template_type=? && scheduled_frequency_type<>? && (scheduled_end_time IS NULL OR scheduled_end_time>=?))) && tag_ids LIKE ?", uid, false, models.TRANSACTION_TEMPLATE_TYPE_NORMAL, models.TRANSACTION_TEMPLATE_TYPE_SCHEDULE, models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_DISABLED, now, "%%"+utils.Int64ToString(tagId)+"%%").Find(&relatedTransactionTemplatesByTag) + + if err != nil { + return err + } + + for i := 0; i < len(relatedTransactionTemplatesByTag); i++ { + template := relatedTransactionTemplatesByTag[i] + tagIds, err := s.GetTagIds(template.TagIds) + + if err != nil { + return err + } + + for j := 0; j < len(tagIds); j++ { + if tagIds[j] == tagId { + return errs.ErrTransactionTagInUseCannotBeDeleted + } + } + } + deletedRows, err := sess.ID(tagId).Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).Update(updateModel) if err != nil {