diff --git a/pkg/api/accounts.go b/pkg/api/accounts.go index 67d5be84..ec2b70df 100644 --- a/pkg/api/accounts.go +++ b/pkg/api/accounts.go @@ -8,6 +8,7 @@ import ( "github.com/mayswind/lab/pkg/log" "github.com/mayswind/lab/pkg/models" "github.com/mayswind/lab/pkg/services" + "github.com/mayswind/lab/pkg/validators" ) type AccountsApi struct { @@ -86,6 +87,25 @@ func (a *AccountsApi) AccountCreateHandler(c *core.Context) (interface{}, *errs. log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] account does not have any sub accounts") return nil, errs.ErrAccountHaveNoSubAccount } + + if accountCreateReq.Currency != validators.PARENT_ACCOUNT_CURRENCY_PLACEHODLER { + log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] parent account cannot set currency") + return nil, errs.ErrParentAccountCannotSetCurrency + } + + for i := 0; i < len(accountCreateReq.SubAccounts); i++ { + subAccount := accountCreateReq.SubAccounts[i] + + if subAccount.Category != accountCreateReq.Category { + log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] category of sub account not equals to parent") + return nil, errs.ErrSubAccountCategoryNotEqualsToParent + } + + if subAccount.Type != models.ACCOUNT_TYPE_SINGLE_ACCOUNT { + log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] sub account type invalid") + return nil, errs.ErrSubAccountTypeInvalid + } + } } else { log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] account type invalid, type is %d", accountCreateReq.Type) return nil, errs.ErrAccountTypeInvalid @@ -206,6 +226,7 @@ func (a *AccountsApi) createNewAccount(uid int64, accountCreateReq *models.Accou Name: accountCreateReq.Name, DisplayOrder: order, Category: accountCreateReq.Category, + Type: accountCreateReq.Type, Icon: accountCreateReq.Icon, Currency: accountCreateReq.Currency, Comment: accountCreateReq.Comment, diff --git a/pkg/errs/account.go b/pkg/errs/account.go index 3b606a50..468792e6 100644 --- a/pkg/errs/account.go +++ b/pkg/errs/account.go @@ -3,9 +3,12 @@ package errs import "net/http" var ( - ErrAccountIdInvalid = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 0, http.StatusBadRequest, "account id is invalid") - ErrAccountNotFound = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 1, http.StatusBadRequest, "account not found") - ErrAccountTypeInvalid = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 2, http.StatusBadRequest, "account type is invalid") - ErrAccountHaveNoSubAccount = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 3, http.StatusBadRequest, "account must have at least one sub account") - ErrAccountCannotHaveSubAccounts = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 4, http.StatusBadRequest, "account cannot have sub accounts") + ErrAccountIdInvalid = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 0, http.StatusBadRequest, "account id is invalid") + ErrAccountNotFound = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 1, http.StatusBadRequest, "account not found") + ErrAccountTypeInvalid = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 2, http.StatusBadRequest, "account type is invalid") + ErrAccountHaveNoSubAccount = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 3, http.StatusBadRequest, "account must have at least one sub account") + ErrAccountCannotHaveSubAccounts = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 4, http.StatusBadRequest, "account cannot have sub accounts") + ErrParentAccountCannotSetCurrency = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 5, http.StatusBadRequest, "parent account cannot set currency") + ErrSubAccountCategoryNotEqualsToParent = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 6, http.StatusBadRequest, "sub account category not equals to parent") + ErrSubAccountTypeInvalid = NewNormalError(NORMAL_SUBCATEGORY_ACCOUNT, 7, http.StatusBadRequest, "sub account type invalid") ) diff --git a/pkg/validators/currency.go b/pkg/validators/currency.go index 7f48b724..5b6b4e8b 100644 --- a/pkg/validators/currency.go +++ b/pkg/validators/currency.go @@ -4,6 +4,8 @@ import ( "github.com/go-playground/validator/v10" ) +const PARENT_ACCOUNT_CURRENCY_PLACEHODLER = "---" + // ISO 4217 var ALL_CURRENCY_NAMES = map[string]bool { "AED": true, //UAE Dirham @@ -167,6 +169,10 @@ var ALL_CURRENCY_NAMES = map[string]bool { func ValidCurrency(fl validator.FieldLevel) bool { if value, ok := fl.Field().Interface().(string); ok { + if value == PARENT_ACCOUNT_CURRENCY_PLACEHODLER { + return true + } + _, ok := ALL_CURRENCY_NAMES[value] return ok } diff --git a/src/consts/account.js b/src/consts/account.js index 3d4b255f..34c7a58d 100644 --- a/src/consts/account.js +++ b/src/consts/account.js @@ -35,7 +35,12 @@ const allAccountCategories = [ defaultAccountIconId: '6' } ]; +const allAccountTypes = { + SingleAccount: 1, + MultiSubAccounts: 2 +}; export default { - allCategories: allAccountCategories + allCategories: allAccountCategories, + allAccountTypes: allAccountTypes, }; diff --git a/src/consts/currency.js b/src/consts/currency.js index 44d9f34e..0c2ff94a 100644 --- a/src/consts/currency.js +++ b/src/consts/currency.js @@ -1,3 +1,5 @@ +const parentAccountCurrencyPlacehodler = '---'; + // ISO 4217 const allCurrencies = [ 'AED', //UAE Dirham @@ -160,5 +162,6 @@ const allCurrencies = [ ]; export default { + parentAccountCurrencyPlacehodler: parentAccountCurrencyPlacehodler, all: allCurrencies }; diff --git a/src/consts/icon.js b/src/consts/icon.js index a9857cbc..fbb86a23 100644 --- a/src/consts/icon.js +++ b/src/consts/icon.js @@ -1,7 +1,5 @@ const totalAccountIconCount = 22; -const defaultAccountIcon = { - f7Icon: 'bag' -}; +const defaultAccountIconId = '1'; const allAccountIcons = { '1': { f7Icon: 'bag' @@ -70,6 +68,7 @@ const allAccountIcons = { export default { allAccountIcons: allAccountIcons, - defaultAccountIcon: defaultAccountIcon, + defaultAccountIconId: defaultAccountIconId, + defaultAccountIcon: allAccountIcons[defaultAccountIconId], totalAccountIconCount: totalAccountIconCount, }; diff --git a/src/locales/en.js b/src/locales/en.js index 795e9bbe..7723639d 100644 --- a/src/locales/en.js +++ b/src/locales/en.js @@ -210,6 +210,9 @@ export default { 'account type is invalid': 'Account type is invalid', 'account must have at least one sub account': 'Account must have at least one sub account', 'account cannot have sub accounts': 'Account cannot have sub accounts', + 'parent account cannot set currency': 'Parent account cannot set currency', + 'sub account category not equals to parent': 'Sub account category does not equal to parent', + 'sub account type invalid': 'Sub account type is invalid', }, 'parameter': { 'id': 'ID', @@ -315,10 +318,16 @@ export default { 'Account Type': 'Account Type', 'Account Name': 'Account Name', 'Your account name': 'Your account name', + 'Sub Account Name': 'Sub Account Name', + 'Your sub account name': 'Your sub account name', 'Account Icon': 'Account Icon', + 'Sub Account Icon': 'Sub Account Icon', 'Currency': 'Currency', 'Description': 'Description', 'Your account description (optional)': 'Your account description (optional)', + 'Your sub account description (optional)': 'Your sub account description (optional)', + 'Add Sub Account': 'Add Sub Account', + 'Remove Sub Account': 'Remove Sub Account', 'Account category cannot be empty': 'Account category cannot be empty', 'Account type cannot be empty': 'Account type cannot be empty', 'Account name cannot be empty': 'Account name cannot be empty', diff --git a/src/locales/zh_Hans.js b/src/locales/zh_Hans.js index e511209f..f6264565 100644 --- a/src/locales/zh_Hans.js +++ b/src/locales/zh_Hans.js @@ -210,6 +210,9 @@ export default { 'account type is invalid': '账户类型无效', 'account must have at least one sub account': '账户必须包含至少一个子账户', 'account cannot have sub accounts': '账户不能包含子账户', + 'parent account cannot set currency': '父账户不能设置货币', + 'sub account category not equals to parent': '子账户类别与父账户不同', + 'sub account type invalid': '子账户类型无效', }, 'parameter': { 'id': 'ID', @@ -315,10 +318,16 @@ export default { 'Account Type': '账户类型', 'Account Name': '账户名称', 'Your account name': '你的账户名称', + 'Sub Account Name': '子账户名称', + 'Your sub account name': '你的子账户名称', 'Account Icon': '账户图标', + 'Sub Account Icon': '子账户图标', 'Currency': '货币', 'Description': '描述', 'Your account description (optional)': '你的账户描述 (可选)', + 'Your sub account description (optional)': '你的子账户描述 (可选)', + 'Add Sub Account': '添加子账户', + 'Remove Sub Account': '删除子账户', 'Account category cannot be empty': '账户分类不能为空', 'Account type cannot be empty': '账户类型不能为空', 'Account name cannot be empty': '账户名称不能为空', diff --git a/src/mobile-main.js b/src/mobile-main.js index f616035e..e381454b 100644 --- a/src/mobile-main.js +++ b/src/mobile-main.js @@ -35,6 +35,7 @@ const i18n = new VueI18n(getI18nOptions()); Vue.prototype.$version = version.getVersion; Vue.prototype.$constants = { + currency: currency, icons: icons, account: account, }; diff --git a/src/views/mobile/accounts/AccountAdd.vue b/src/views/mobile/accounts/AccountAdd.vue index 13d60b4b..e83f3515 100644 --- a/src/views/mobile/accounts/AccountAdd.vue +++ b/src/views/mobile/accounts/AccountAdd.vue @@ -4,7 +4,7 @@ - + @@ -24,7 +24,6 @@ - + + @click="showIconSelectionSheet(account)"> @@ -71,14 +70,86 @@ :value="account.comment" @input="account.comment = $event.target.value" > - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -89,8 +160,8 @@ - - + + @@ -110,12 +181,14 @@ export default { return { account: { category: '1', - type: '1', + type: self.$constants.account.allAccountTypes.SingleAccount.toString(), name: '', - icon: "1", + icon: self.$constants.icons.defaultAccountIconId, currency: self.$user.getUserInfo() ? self.$user.getUserInfo().defaultCurrency : self.$t('default.currency'), comment: '' }, + subAccounts: [], + accountChoosingIcon: null, submitting: false, showIconSelection: false }; @@ -154,36 +227,54 @@ export default { }, allCurrencies() { return this.$getAllCurrencies(); - }, - inputIsEmpty() { - return !!this.inputEmptyProblemMessage; - }, - inputIsInvalid() { - return !!this.inputInvalidProblemMessage; - }, - inputEmptyProblemMessage() { - if (!this.account.category) { - return 'Account category cannot be empty'; - } else if (!this.account.type) { - return 'Account type cannot be empty'; - } else if (!this.account.name) { - return 'Account name cannot be empty'; - } else if (!this.account.currency) { - return 'Account currency cannot be empty'; - } else { - return null; - } - }, - inputInvalidProblemMessage() { - return null; } }, methods: { + addSubAccount() { + const self = this; + + if (self.account.type !== self.$constants.account.allAccountTypes.MultiSubAccounts.toString()) { + return; + } + + this.subAccounts.push({ + category: null, + type: null, + name: '', + icon: this.account.icon, + currency: self.$user.getUserInfo() ? self.$user.getUserInfo().defaultCurrency : self.$t('default.currency'), + comment: '' + }); + }, + removeSubAccount(subAccount) { + for (let i = 0; i < this.subAccounts.length; i++) { + if (this.subAccounts[i] === subAccount) { + this.subAccounts.splice(i, 1); + } + } + }, + showIconSelectionSheet(account) { + this.accountChoosingIcon = account; + this.showIconSelection = true + }, + setSelectedIcon(accountIcon) { + if (!this.accountChoosingIcon) { + return; + } + + this.accountChoosingIcon.icon = accountIcon.id; + this.accountChoosingIcon = null; + this.showIconSelection = false; + }, + hideIconSelectionSheet() { + this.accountChoosingIcon = null; + this.showIconSelection = false; + }, add() { const self = this; const router = self.$f7router; - let problemMessage = self.inputEmptyProblemMessage || self.inputInvalidProblemMessage; + let problemMessage = self.inputEmptyProblemMessage; if (problemMessage) { self.$alert(problemMessage); @@ -193,13 +284,31 @@ export default { self.submitting = true; self.$showLoading(() => self.submitting); + const subAccounts = []; + + if (self.account.type === self.$constants.account.allAccountTypes.MultiSubAccounts.toString()) { + for (let i = 0; i < self.subAccounts.length; i++) { + const subAccount = self.subAccounts[i]; + + subAccounts.push({ + category: parseInt(self.account.category), + type: self.$constants.account.allAccountTypes.SingleAccount, + name: subAccount.name, + icon: subAccount.icon, + currency: subAccount.currency, + comment: subAccount.comment + }); + } + } + self.$services.addAccount({ category: parseInt(self.account.category), type: parseInt(self.account.type), name: self.account.name, icon: self.account.icon, - currency: self.account.currency, - comment: self.account.comment + currency: self.account.type === self.$constants.account.allAccountTypes.SingleAccount.toString() ? self.account.currency : self.$constants.currency.parentAccountCurrencyPlacehodler, + comment: self.account.comment, + subAccounts: self.account.type === self.$constants.account.allAccountTypes.SingleAccount.toString() ? null : subAccounts, }).then(response => { self.submitting = false; self.$hideLoading(); @@ -241,6 +350,38 @@ export default { this.account.icon = allCategories[i].defaultAccountIconId; } } + }, + isInputEmpty() { + const isAccountEmpty = !!this.getInputEmptyProblemMessage(this.account, false); + + if (isAccountEmpty) { + return true; + } + + if (this.account.type === this.$constants.account.allAccountTypes.MultiSubAccounts.toString()) { + for (let i = 0; i < this.subAccounts.length; i++) { + const isSubAccountEmpty = !!this.getInputEmptyProblemMessage(this.subAccounts[i], true); + + if (isSubAccountEmpty) { + return true; + } + } + } + + return false; + }, + getInputEmptyProblemMessage(account, isSubAccount) { + if (!isSubAccount && !account.category) { + return 'Account category cannot be empty'; + } else if (!isSubAccount && !account.type) { + return 'Account type cannot be empty'; + } else if (!account.name) { + return 'Account name cannot be empty'; + } else if (account.type === this.$constants.account.allAccountTypes.SingleAccount.toString() && !account.currency) { + return 'Account currency cannot be empty'; + } else { + return null; + } } } }