From 0e391bee504cf93f048a752522493d310fb208ed Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 23 Jun 2024 23:37:58 +0800 Subject: [PATCH] support changing primary category for transaction category --- pkg/api/transaction_categories.go | 51 +++++++++++++++---- pkg/errs/transaction_category.go | 18 ++++--- pkg/models/transaction_category.go | 13 ++--- pkg/services/transaction_categories.go | 2 +- src/lib/category.js | 14 +++++ src/lib/services.js | 3 +- src/locales/en.js | 6 +++ src/locales/zh_Hans.js | 6 +++ src/stores/transactionCategory.js | 13 ++++- src/views/desktop/categories/ListPage.vue | 4 ++ .../categories/list/dialogs/EditDialog.vue | 33 +++++++++++- src/views/mobile/categories/EditPage.vue | 31 ++++++++++- 12 files changed, 166 insertions(+), 28 deletions(-) diff --git a/pkg/api/transaction_categories.go b/pkg/api/transaction_categories.go index 2d1ca547..c2e57152 100644 --- a/pkg/api/transaction_categories.go +++ b/pkg/api/transaction_categories.go @@ -173,16 +173,18 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (any, } newCategory := &models.TransactionCategory{ - CategoryId: category.CategoryId, - Uid: uid, - Name: categoryModifyReq.Name, - Icon: categoryModifyReq.Icon, - Color: categoryModifyReq.Color, - Comment: categoryModifyReq.Comment, - Hidden: categoryModifyReq.Hidden, + CategoryId: category.CategoryId, + Uid: uid, + ParentCategoryId: categoryModifyReq.ParentId, + Name: categoryModifyReq.Name, + Icon: categoryModifyReq.Icon, + Color: categoryModifyReq.Color, + Comment: categoryModifyReq.Comment, + Hidden: categoryModifyReq.Hidden, } - if newCategory.Name == category.Name && + if newCategory.ParentCategoryId == category.ParentCategoryId && + newCategory.Name == category.Name && newCategory.Icon == category.Icon && newCategory.Color == category.Color && newCategory.Comment == category.Comment && @@ -190,6 +192,38 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (any, return nil, errs.ErrNothingWillBeUpdated } + if category.ParentCategoryId == 0 && newCategory.ParentCategoryId != 0 { + return nil, errs.Or(err, errs.ErrNotAllowChangePrimaryTransactionCategoryToSecondary) + } + + if category.ParentCategoryId != 0 && newCategory.ParentCategoryId == 0 { + return nil, errs.Or(err, errs.ErrNotAllowChangeSecondaryTransactionCategoryToPrimary) + } + + if newCategory.ParentCategoryId != category.ParentCategoryId { + fromPrimaryCategory, err := a.categories.GetCategoryByCategoryId(c, uid, category.ParentCategoryId) + + if err != nil { + log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to get old primary category \"id:%d\" of category \"id:%d\" for user \"uid:%d\", because %s", category.ParentCategoryId, categoryModifyReq.Id, uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) + } + + toPrimaryCategory, err := a.categories.GetCategoryByCategoryId(c, uid, newCategory.ParentCategoryId) + + if err != nil { + log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to get new primary category \"id:%d\" of category \"id:%d\" for user \"uid:%d\", because %s", newCategory.ParentCategoryId, categoryModifyReq.Id, uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) + } + + if fromPrimaryCategory.Type != toPrimaryCategory.Type { + return nil, errs.Or(err, errs.ErrNotAllowChangePrimaryTransactionType) + } + + if toPrimaryCategory.ParentCategoryId != 0 { + return nil, errs.Or(err, errs.ErrNotAllowUseSecondaryTransactionAsPrimaryCategory) + } + } + err = a.categories.ModifyCategory(c, newCategory) if err != nil { @@ -200,7 +234,6 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (any, log.InfofWithRequestId(c, "[transaction_categories.CategoryModifyHandler] user \"uid:%d\" has updated category \"id:%d\" successfully", uid, categoryModifyReq.Id) newCategory.Type = category.Type - newCategory.ParentCategoryId = category.ParentCategoryId newCategory.DisplayOrder = category.DisplayOrder categoryResp := newCategory.ToTransactionCategoryInfoResponse() diff --git a/pkg/errs/transaction_category.go b/pkg/errs/transaction_category.go index 538fe851..4e826f0b 100644 --- a/pkg/errs/transaction_category.go +++ b/pkg/errs/transaction_category.go @@ -4,11 +4,15 @@ import "net/http" // Error codes related to transaction categories var ( - ErrTransactionCategoryIdInvalid = NewNormalError(NormalSubcategoryCategory, 0, http.StatusBadRequest, "transaction category id is invalid") - ErrTransactionCategoryNotFound = NewNormalError(NormalSubcategoryCategory, 1, http.StatusBadRequest, "transaction category not found") - ErrTransactionCategoryTypeInvalid = NewNormalError(NormalSubcategoryCategory, 2, http.StatusBadRequest, "transaction category type is invalid") - ErrParentTransactionCategoryNotFound = NewNormalError(NormalSubcategoryCategory, 3, http.StatusBadRequest, "parent transaction category not found") - ErrCannotAddToSecondaryTransactionCategory = NewNormalError(NormalSubcategoryCategory, 4, http.StatusBadRequest, "cannot add to secondary transaction category") - ErrCannotUsePrimaryCategoryForTransaction = NewNormalError(NormalSubcategoryCategory, 5, http.StatusBadRequest, "cannot use primary category for transaction category") - ErrTransactionCategoryInUseCannotBeDeleted = NewNormalError(NormalSubcategoryCategory, 6, http.StatusBadRequest, "transaction category is in use and cannot be deleted") + ErrTransactionCategoryIdInvalid = NewNormalError(NormalSubcategoryCategory, 0, http.StatusBadRequest, "transaction category id is invalid") + ErrTransactionCategoryNotFound = NewNormalError(NormalSubcategoryCategory, 1, http.StatusBadRequest, "transaction category not found") + ErrTransactionCategoryTypeInvalid = NewNormalError(NormalSubcategoryCategory, 2, http.StatusBadRequest, "transaction category type is invalid") + ErrParentTransactionCategoryNotFound = NewNormalError(NormalSubcategoryCategory, 3, http.StatusBadRequest, "parent transaction category not found") + ErrCannotAddToSecondaryTransactionCategory = NewNormalError(NormalSubcategoryCategory, 4, http.StatusBadRequest, "cannot add to secondary transaction category") + ErrCannotUsePrimaryCategoryForTransaction = NewNormalError(NormalSubcategoryCategory, 5, http.StatusBadRequest, "cannot use primary category for transaction category") + ErrTransactionCategoryInUseCannotBeDeleted = NewNormalError(NormalSubcategoryCategory, 6, http.StatusBadRequest, "transaction category is in use and cannot be deleted") + ErrNotAllowChangePrimaryTransactionCategoryToSecondary = NewNormalError(NormalSubcategoryCategory, 7, http.StatusBadRequest, "not allow to change primary category to secondary category") + ErrNotAllowChangeSecondaryTransactionCategoryToPrimary = NewNormalError(NormalSubcategoryCategory, 8, http.StatusBadRequest, "not allow to change secondary category to primary category") + ErrNotAllowChangePrimaryTransactionType = NewNormalError(NormalSubcategoryCategory, 9, http.StatusBadRequest, "not allow to change primary category with different type") + ErrNotAllowUseSecondaryTransactionAsPrimaryCategory = NewNormalError(NormalSubcategoryCategory, 10, http.StatusBadRequest, "not allow to use secondary category as primary category") ) diff --git a/pkg/models/transaction_category.go b/pkg/models/transaction_category.go index 5d5c37e0..1e3f0999 100644 --- a/pkg/models/transaction_category.go +++ b/pkg/models/transaction_category.go @@ -69,12 +69,13 @@ type TransactionCategoryCreateWithSubCategories struct { // TransactionCategoryModifyRequest represents all parameters of transaction category modification request type TransactionCategoryModifyRequest struct { - Id int64 `json:"id,string" binding:"required,min=1"` - Name string `json:"name" binding:"required,notBlank,max=32"` - Icon int64 `json:"icon,string" binding:"min=1"` - Color string `json:"color" binding:"required,len=6,validHexRGBColor"` - Comment string `json:"comment" binding:"max=255"` - Hidden bool `json:"hidden"` + Id int64 `json:"id,string" binding:"required,min=1"` + Name string `json:"name" binding:"required,notBlank,max=32"` + ParentId int64 `json:"parentId,string" binding:"min=0"` + Icon int64 `json:"icon,string" binding:"min=1"` + Color string `json:"color" binding:"required,len=6,validHexRGBColor"` + Comment string `json:"comment" binding:"max=255"` + Hidden bool `json:"hidden"` } // TransactionCategoryHideRequest represents all parameters of transaction category hiding request diff --git a/pkg/services/transaction_categories.go b/pkg/services/transaction_categories.go index a215998c..f493e173 100644 --- a/pkg/services/transaction_categories.go +++ b/pkg/services/transaction_categories.go @@ -249,7 +249,7 @@ func (s *TransactionCategoryService) ModifyCategory(c *core.Context, category *m category.UpdatedUnixTime = time.Now().Unix() return s.UserDataDB(category.Uid).DoTransaction(c, func(sess *xorm.Session) error { - updatedRows, err := sess.ID(category.CategoryId).Cols("name", "icon", "color", "comment", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", category.Uid, false).Update(category) + updatedRows, err := sess.ID(category.CategoryId).Cols("parent_category_id", "name", "icon", "color", "comment", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", category.Uid, false).Update(category) if err != nil { return err diff --git a/src/lib/category.js b/src/lib/category.js index 9fde29d1..5543d200 100644 --- a/src/lib/category.js +++ b/src/lib/category.js @@ -116,6 +116,20 @@ export function allVisibleTransactionCategories(allTransactionCategories) { return ret; } +export function allVisiblePrimaryTransactionCategoriesByType(allTransactionCategories, type) { + const allVisibleCategories = allVisibleTransactionCategories(allTransactionCategories); + + if (!allVisibleCategories) { + return []; + } + + if (!allVisibleCategories[type.toString()]) { + return []; + } + + return allVisibleCategories[type.toString()].visibleCategories; +} + export function isSubCategoryIdAvailable(categories, categoryId) { if (!categories || !categories.length) { return false; diff --git a/src/lib/services.js b/src/lib/services.js index 65e2826c..1b24ea1b 100644 --- a/src/lib/services.js +++ b/src/lib/services.js @@ -436,10 +436,11 @@ export default { categories }); }, - modifyTransactionCategory: ({ id, name, icon, color, comment, hidden }) => { + modifyTransactionCategory: ({ id, name, parentId, icon, color, comment, hidden }) => { return axios.post('v1/transaction/categories/modify.json', { id, name, + parentId, icon, color, comment, diff --git a/src/locales/en.js b/src/locales/en.js index 4e45e9d4..bd95f2e0 100644 --- a/src/locales/en.js +++ b/src/locales/en.js @@ -648,6 +648,10 @@ export default { 'cannot add to secondary transaction category': 'Cannot add transaction category to secondary transaction category', 'cannot use primary category for transaction category': 'Cannot use primary category for transaction category', 'transaction category is in use and cannot be deleted': 'Transaction category is in use and it cannot be deleted', + 'not allow to change primary category to secondary category': 'Not allow to change primary category to secondary category', + 'not allow to change secondary category to primary category': 'Not allow to change secondary category to primary category', + 'not allow to change primary category with different type': 'Not allow to change primary category with different type', + 'not allow to use secondary category as primary category': 'Not allow to use secondary category as primary category', 'transaction tag id is invalid': 'Transaction tag ID is invalid', 'transaction tag not found': 'Transaction tag is not found', 'transaction tag name is empty': 'Transaction tag title is empty', @@ -1153,6 +1157,7 @@ export default { 'Unable to unhide this category': 'Unable to unhide this category', 'Are you sure you want to delete this category?': 'Are you sure you want to delete this category?', 'Unable to delete this category': 'Unable to delete this category', + 'Primary Category': 'Primary Category', 'Add Primary Category': 'Add Primary Category', 'Add Secondary Category': 'Add Secondary Category', 'Edit Category': 'Edit Category', @@ -1161,6 +1166,7 @@ export default { 'Category Icon': 'Category Icon', 'Category Color': 'Category Color', 'Your category description (optional)': 'Your category description (optional)', + 'No available primary category': 'No available primary category', 'Category name cannot be blank': 'Category name cannot be blank', 'Unable to retrieve category': 'Unable to retrieve category', 'Unable to add category': 'Unable to add category', diff --git a/src/locales/zh_Hans.js b/src/locales/zh_Hans.js index 591d814d..15e06409 100644 --- a/src/locales/zh_Hans.js +++ b/src/locales/zh_Hans.js @@ -648,6 +648,10 @@ export default { 'cannot add to secondary transaction category': '不能在二级交易分类中添加', 'cannot use primary category for transaction category': '交易分类不能使用一级分类', 'transaction category is in use and cannot be deleted': '交易分类正在被使用,无法删除', + 'not allow to change primary category to secondary category': '不允许更改一级分类为二级分类', + 'not allow to change secondary category to primary category': '不允许更改二级分类为一级分类', + 'not allow to change primary category with different type': '不允许更改为不同类型的一级分类', + 'not allow to use secondary category as primary category': '不允许使用二级分类作为一级分类', 'transaction tag id is invalid': '交易标签ID无效', 'transaction tag not found': '交易标签不存在', 'transaction tag name is empty': '交易标签标题不能为空', @@ -1153,6 +1157,7 @@ export default { 'Unable to unhide this category': '无法取消隐藏该分类', 'Are you sure you want to delete this category?': '您确定要删除该分类?', 'Unable to delete this category': '无法删除该分类', + 'Primary Category': '一级分类', 'Add Primary Category': '添加一级分类', 'Add Secondary Category': '添加二级分类', 'Edit Category': '编辑分类', @@ -1161,6 +1166,7 @@ export default { 'Category Icon': '分类图标', 'Category Color': '分类颜色', 'Your category description (optional)': '你的分类描述 (可选)', + 'No available primary category': '没有可用一级分类', 'Category name cannot be blank': '分类名称不能为空', 'Unable to retrieve category': '无法获取分类', 'Unable to add category': '无法添加分类', diff --git a/src/stores/transactionCategory.js b/src/stores/transactionCategory.js index 370f70f7..04e7c825 100644 --- a/src/stores/transactionCategory.js +++ b/src/stores/transactionCategory.js @@ -46,7 +46,11 @@ function addCategoryToTransactionCategoryList(state, category) { state.allTransactionCategoriesMap[category.id] = category; } -function updateCategoryInTransactionCategoryList(state, category) { +function updateCategoryInTransactionCategoryList(state, category, oldCategory) { + if (oldCategory && category.parentId !== oldCategory.parentId) { + return false; + } + let categoryList = null; if (!category.parentId || category.parentId === '0') { @@ -65,6 +69,7 @@ function updateCategoryInTransactionCategoryList(state, category) { } state.allTransactionCategoriesMap[category.id] = category; + return true; } function updateCategoryDisplayOrderInCategoryList(state, { category, from, to }) { @@ -293,7 +298,11 @@ export const useTransactionCategoriesStore = defineStore('transactionCategories' if (!submitCategory.id) { addCategoryToTransactionCategoryList(self, data.result); } else { - updateCategoryInTransactionCategoryList(self, data.result); + const result = updateCategoryInTransactionCategoryList(self, data.result, self.allTransactionCategoriesMap[submitCategory.id]); + + if (!result && !self.transactionCategoryListStateInvalid) { + self.updateTransactionCategoryListInvalidState(true); + } } resolve(data.result); diff --git a/src/views/desktop/categories/ListPage.vue b/src/views/desktop/categories/ListPage.vue index 58594fe8..da13d7cc 100644 --- a/src/views/desktop/categories/ListPage.vue +++ b/src/views/desktop/categories/ListPage.vue @@ -414,6 +414,10 @@ export default { self.$refs.snackbar.showMessage(result.message); } + if (self.transactionCategoriesStore.transactionCategoryListStateInvalid) { + self.reload(true); + } + self.updateCardMinHeight(); }).catch(error => { if (error) { diff --git a/src/views/desktop/categories/list/dialogs/EditDialog.vue b/src/views/desktop/categories/list/dialogs/EditDialog.vue index 5c30658f..0a81c51d 100644 --- a/src/views/desktop/categories/list/dialogs/EditDialog.vue +++ b/src/views/desktop/categories/list/dialogs/EditDialog.vue @@ -21,6 +21,31 @@ v-model="category.name" /> + + + + + +