diff --git a/cmd/database.go b/cmd/database.go
index a18128ef..5fd9c230 100644
--- a/cmd/database.go
+++ b/cmd/database.go
@@ -31,6 +31,7 @@ func updateDatabaseStructure(c *cli.Context) error {
_ = datastore.Container.UserStore.SyncStructs(new(models.User), new(models.TwoFactor), new(models.TwoFactorRecoveryCode))
_ = datastore.Container.TokenStore.SyncStructs(new(models.TokenRecord))
+ _ = datastore.Container.UserDataStore.SyncStructs(new(models.Account))
log.BootInfof("[database.updateDatabaseStructure] maintained successfully")
diff --git a/cmd/webserver.go b/cmd/webserver.go
index 38635e1d..92070dd3 100644
--- a/cmd/webserver.go
+++ b/cmd/webserver.go
@@ -157,6 +157,10 @@ func startWebServer(c *cli.Context) error {
apiV1Route.POST("/users/2fa/disable.json", bindApi(api.TwoFactorAuthorizations.TwoFactorDisableHandler))
apiV1Route.POST("/users/2fa/recovery/regenerate.json", bindApi(api.TwoFactorAuthorizations.TwoFactorRecoveryCodeRegenerateHandler))
}
+
+ // Accounts
+ apiV1Route.GET("/accounts/list.json", bindApi(api.Accounts.AccountListHandler))
+ apiV1Route.POST("/accounts/add.json", bindApi(api.Accounts.AccountCreateHandler))
}
}
diff --git a/pkg/api/accounts.go b/pkg/api/accounts.go
new file mode 100644
index 00000000..d1210258
--- /dev/null
+++ b/pkg/api/accounts.go
@@ -0,0 +1,151 @@
+package api
+
+import (
+ "sort"
+
+ "github.com/mayswind/lab/pkg/core"
+ "github.com/mayswind/lab/pkg/errs"
+ "github.com/mayswind/lab/pkg/log"
+ "github.com/mayswind/lab/pkg/models"
+ "github.com/mayswind/lab/pkg/services"
+)
+
+type AccountsApi struct {
+ accounts *services.AccountService
+}
+
+var (
+ Accounts = &AccountsApi{
+ accounts: services.Accounts,
+ }
+)
+
+func (a *AccountsApi) AccountListHandler(c *core.Context) (interface{}, *errs.Error) {
+ uid := c.GetCurrentUid()
+ accounts, err := a.accounts.GetAllAccountsByUid(uid)
+
+ if err != nil {
+ log.ErrorfWithRequestId(c, "[accounts.AccountListHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
+ return nil, errs.ErrOperationFailed
+ }
+
+ userAllAccountResps := make([]*models.AccountInfoResponse, len(accounts))
+ userAllAccountRespMap := make(map[int64]*models.AccountInfoResponse)
+
+ for i := 0; i < len(accounts); i++ {
+ userAllAccountResps[i] = accounts[i].ToAccountInfoResponse()
+ userAllAccountRespMap[userAllAccountResps[i].Id] = userAllAccountResps[i]
+ }
+
+ for i := 0; i < len(userAllAccountResps); i++ {
+ userAccountResp := userAllAccountResps[i]
+
+ if userAccountResp.ParentId <= models.ACCOUNT_PARENT_ID_LEVEL_ONE {
+ continue
+ }
+
+ parentAccount, parentExists := userAllAccountRespMap[userAccountResp.ParentId]
+
+ if !parentExists || parentAccount == nil {
+ continue
+ }
+
+ parentAccount.SubAccounts = append(parentAccount.SubAccounts, userAccountResp)
+ }
+
+ userFinalAccountResps := make(models.AccountInfoResponseSlice, 0)
+
+ for i := 0; i < len(userAllAccountResps); i++ {
+ if userAllAccountResps[i].ParentId == models.ACCOUNT_PARENT_ID_LEVEL_ONE {
+ sort.Sort(userAllAccountResps[i].SubAccounts)
+ userFinalAccountResps = append(userFinalAccountResps, userAllAccountResps[i])
+ }
+ }
+
+ sort.Sort(userFinalAccountResps)
+
+ return userFinalAccountResps, nil
+}
+
+func (a *AccountsApi) AccountCreateHandler(c *core.Context) (interface{}, *errs.Error) {
+ var accountCreateReq models.AccountCreateRequest
+ err := c.ShouldBindJSON(&accountCreateReq)
+
+ if err != nil {
+ log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] parse request failed, because %s", err.Error())
+ return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
+ }
+
+ if accountCreateReq.Type == models.ACCOUNT_TYPE_SINGLE_ACCOUNT {
+ if len(accountCreateReq.SubAccounts) > 0 {
+ log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] account cannot have any sub accounts")
+ return nil, errs.ErrAccountCannotHaveSubAccounts
+ }
+ } else if accountCreateReq.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
+ if len(accountCreateReq.SubAccounts) < 1 {
+ log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] account does not have any sub accounts")
+ return nil, errs.ErrAccountHaveNoSubAccount
+ }
+ } else {
+ log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] account type invalid, type is %d", accountCreateReq.Type)
+ return nil, errs.ErrAccountTypeInvalid
+ }
+
+ uid := c.GetCurrentUid()
+ maxOrderId, err := a.accounts.GetMaxDisplayOrder(uid)
+
+ if err != nil {
+ log.ErrorfWithRequestId(c, "[accounts.AccountCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
+ return nil, errs.ErrOperationFailed
+ }
+
+ mainAccount := a.createNewAccount(uid, &accountCreateReq, maxOrderId+1)
+ childrenAccounts := a.createSubAccounts(uid, &accountCreateReq)
+
+ err = a.accounts.CreateAccounts(mainAccount, childrenAccounts)
+
+ if err != nil {
+ log.ErrorfWithRequestId(c, "[accounts.AccountCreateHandler] failed to create account \"id:%d\" for user \"uid:%d\", because %s", mainAccount.AccountId, uid, err.Error())
+ return nil, errs.Or(err, errs.ErrOperationFailed)
+ }
+
+ log.InfofWithRequestId(c, "[accounts.AccountCreateHandler] user \"uid:%d\" has created a new account \"id:%d\" successfully", uid, mainAccount.AccountId)
+
+ accountInfoResp := mainAccount.ToAccountInfoResponse()
+
+ if len(childrenAccounts) > 0 {
+ accountInfoResp.SubAccounts = make([]*models.AccountInfoResponse, len(childrenAccounts))
+
+ for i := 0; i < len(childrenAccounts); i++ {
+ accountInfoResp.SubAccounts[i] = childrenAccounts[i].ToAccountInfoResponse()
+ }
+ }
+
+ return accountInfoResp, nil
+}
+
+func (a *AccountsApi) createNewAccount(uid int64, accountCreateReq *models.AccountCreateRequest, order int) *models.Account {
+ return &models.Account{
+ Uid: uid,
+ Name: accountCreateReq.Name,
+ DisplayOrder: order,
+ Category: accountCreateReq.Category,
+ Icon: accountCreateReq.Icon,
+ Currency: accountCreateReq.Currency,
+ Comment: accountCreateReq.Comment,
+ }
+}
+
+func (a *AccountsApi) createSubAccounts(uid int64, accountCreateReq *models.AccountCreateRequest) []*models.Account {
+ if len(accountCreateReq.SubAccounts) <= 0 {
+ return nil
+ }
+
+ childrenAccounts := make([]*models.Account, len(accountCreateReq.SubAccounts))
+
+ for i := 0; i < len(accountCreateReq.SubAccounts); i++ {
+ childrenAccounts[i] = a.createNewAccount(uid, accountCreateReq.SubAccounts[i], i+1)
+ }
+
+ return childrenAccounts
+}
diff --git a/pkg/errs/account.go b/pkg/errs/account.go
new file mode 100644
index 00000000..3b606a50
--- /dev/null
+++ b/pkg/errs/account.go
@@ -0,0 +1,11 @@
+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")
+)
diff --git a/pkg/models/account.go b/pkg/models/account.go
new file mode 100644
index 00000000..e24f48f9
--- /dev/null
+++ b/pkg/models/account.go
@@ -0,0 +1,97 @@
+package models
+
+// Level-One Account
+const ACCOUNT_PARENT_ID_LEVEL_ONE = 0
+
+type AccountCategory byte
+
+const (
+ ACCOUNT_CATEGORY_CASH AccountCategory = 1
+ ACCOUNT_CATEGORY_DEBIT_CARD AccountCategory = 2
+ ACCOUNT_CATEGORY_CREDIT_CARD AccountCategory = 3
+ ACCOUNT_CATEGORY_VIRTUAL AccountCategory = 4
+ ACCOUNT_CATEGORY_DEBT AccountCategory = 5
+ ACCOUNT_CATEGORY_RECEIVABLES AccountCategory = 6
+ ACCOUNT_CATEGORY_INVESTMENT AccountCategory = 7
+)
+
+type AccountType byte
+
+const (
+ ACCOUNT_TYPE_SINGLE_ACCOUNT AccountType = 1
+ ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS AccountType = 2
+)
+
+type Account struct {
+ AccountId int64 `xorm:"PK"`
+ Uid int64 `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
+ Deleted bool `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
+ Category AccountCategory `xorm:"NOT NULL"`
+ Type AccountType `xorm:"NOT NULL"`
+ ParentAccountId int64 `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
+ Name string `xorm:"VARCHAR(32) NOT NULL"`
+ DisplayOrder int `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
+ Icon int64 `xorm:"NOT NULL"`
+ Currency string `xorm:"VARCHAR(3) NOT NULL"`
+ Balance int64 `xorm:"NOT NULL"`
+ Comment string `xorm:"VARCHAR(255) NOT NULL"`
+ Hidden bool `xorm:"NOT NULL"`
+ CreatedUnixTime int64
+ UpdatedUnixTime int64
+ DeletedUnixTime int64
+}
+
+type AccountCreateRequest struct {
+ Name string `json:"name" binding:"required,notBlank,max=32"`
+ Category AccountCategory `json:"category" binding:"required"`
+ Type AccountType `json:"type" binding:"required"`
+ Icon int64 `json:"icon,string" binding:"required,min=1"`
+ Currency string `json:"currency" binding:"required,len=3,validCurrency"`
+ Comment string `json:"comment" binding:"max=255"`
+ SubAccounts []*AccountCreateRequest `json:"subAccounts" binding:"omitempty"`
+}
+
+type AccountInfoResponse struct {
+ Id int64 `json:"id,string"`
+ Name string `json:"name"`
+ ParentId int64 `json:"parentId,string"`
+ Category AccountCategory `json:"category"`
+ Type AccountType `json:"type"`
+ Icon int64 `json:"icon,string"`
+ Currency string `json:"currency"`
+ Balance int64 `json:"balance"`
+ Comment string `json:"comment"`
+ DisplayOrder int `json:"displayOrder"`
+ Hidden bool `json:"hidden"`
+ SubAccounts AccountInfoResponseSlice `json:"subAccounts,omitempty"`
+}
+
+func (a *Account) ToAccountInfoResponse() *AccountInfoResponse {
+ return &AccountInfoResponse{
+ Id: a.AccountId,
+ Name: a.Name,
+ ParentId: a.ParentAccountId,
+ Category: a.Category,
+ Type: a.Type,
+ Icon: a.Icon,
+ Currency: a.Currency,
+ Balance: a.Balance,
+ Comment: a.Comment,
+ DisplayOrder: a.DisplayOrder,
+ Hidden: a.Hidden,
+ }
+}
+
+type AccountInfoResponseSlice []*AccountInfoResponse
+
+func (a AccountInfoResponseSlice) Len() int {
+ return len(a)
+}
+
+func (a AccountInfoResponseSlice) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
+func (a AccountInfoResponseSlice) Less(i, j int) bool {
+ return a[i].DisplayOrder < a[j].DisplayOrder
+}
diff --git a/pkg/services/accounts.go b/pkg/services/accounts.go
new file mode 100644
index 00000000..44fc0b7e
--- /dev/null
+++ b/pkg/services/accounts.go
@@ -0,0 +1,123 @@
+package services
+
+import (
+ "time"
+
+ "xorm.io/xorm"
+
+ "github.com/mayswind/lab/pkg/datastore"
+ "github.com/mayswind/lab/pkg/errs"
+ "github.com/mayswind/lab/pkg/models"
+ "github.com/mayswind/lab/pkg/uuid"
+)
+
+type AccountService struct {
+ ServiceUsingDB
+ ServiceUsingUuid
+}
+
+var (
+ Accounts = &AccountService{
+ ServiceUsingDB: ServiceUsingDB{
+ container: datastore.Container,
+ },
+ ServiceUsingUuid: ServiceUsingUuid{
+ container: uuid.Container,
+ },
+ }
+)
+
+func (s *AccountService) GetAllAccountsByUid(uid int64) ([]*models.Account, error) {
+ if uid <= 0 {
+ return nil, errs.ErrUserIdInvalid
+ }
+
+ var accounts []*models.Account
+ err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).OrderBy("parent_account_id asc, display_order asc").Find(&accounts)
+
+ return accounts, err
+}
+
+func (s *AccountService) GetMaxDisplayOrder(uid int64) (int, error) {
+ if uid <= 0 {
+ return 0, errs.ErrUserIdInvalid
+ }
+
+ account := &models.Account{}
+ has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_account_id", "display_order").Where("uid=? AND deleted=? AND parent_account_id=?", uid, false, models.ACCOUNT_PARENT_ID_LEVEL_ONE).OrderBy("display_order desc").Limit(1).Get(account)
+
+ if err != nil {
+ return 0, err
+ }
+
+ if has {
+ return account.DisplayOrder, nil
+ } else {
+ return 0, nil
+ }
+}
+
+func (s *AccountService) GetMaxSubAccountDisplayOrder(uid int64, parentAccountId int64) (int, error) {
+ if uid <= 0 {
+ return 0, errs.ErrUserIdInvalid
+ }
+
+ if parentAccountId <= 0 {
+ return 0, errs.ErrAccountIdInvalid
+ }
+
+ account := &models.Account{}
+ has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_account_id", "display_order").Where("uid=? AND deleted=? AND parent_account_id=?", uid, false, parentAccountId).OrderBy("display_order desc").Limit(1).Get(account)
+
+ if err != nil {
+ return 0, err
+ }
+
+ if has {
+ return account.DisplayOrder, nil
+ } else {
+ return 0, nil
+ }
+}
+
+func (s *AccountService) CreateAccounts(mainAccount *models.Account, childrenAccounts []*models.Account) error {
+ if mainAccount.Uid <= 0 {
+ return errs.ErrUserIdInvalid
+ }
+
+ allAccounts := make([]*models.Account, len(childrenAccounts)+1)
+
+ mainAccount.AccountId = s.GenerateUuid(uuid.UUID_TYPE_ACCOUNT)
+ allAccounts[0] = mainAccount
+
+ if mainAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
+ for i := 0; i < len(childrenAccounts); i++ {
+ childAccount := childrenAccounts[i]
+ childAccount.AccountId = s.GenerateUuid(uuid.UUID_TYPE_ACCOUNT)
+ childAccount.ParentAccountId = mainAccount.AccountId
+ childAccount.Uid = mainAccount.Uid
+ childAccount.Type = models.ACCOUNT_TYPE_SINGLE_ACCOUNT
+
+ allAccounts[i+1] = childrenAccounts[i]
+ }
+ }
+
+ for i := 0; i < len(allAccounts); i++ {
+ allAccounts[i].Deleted = false
+ allAccounts[i].CreatedUnixTime = time.Now().Unix()
+ allAccounts[i].UpdatedUnixTime = time.Now().Unix()
+ }
+
+ return s.UserDataDB(mainAccount.Uid).DoTransaction(func(sess *xorm.Session) error {
+ for i := 0; i < len(allAccounts); i++ {
+ account := allAccounts[i]
+ _, err := sess.Insert(account)
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ })
+}
diff --git a/src/consts/account.js b/src/consts/account.js
new file mode 100644
index 00000000..26bb7bd0
--- /dev/null
+++ b/src/consts/account.js
@@ -0,0 +1,34 @@
+const allAccountCategories = [
+ {
+ id: 1,
+ name: 'Cash'
+ },
+ {
+ id: 2,
+ name: 'Debit Card'
+ },
+ {
+ id: 3,
+ name: 'Credit Card'
+ },
+ {
+ id: 4,
+ name: 'Virtual Account'
+ },
+ {
+ id: 5,
+ name: 'Debt Account'
+ },
+ {
+ id: 6,
+ name: 'Receivables'
+ },
+ {
+ id: 7,
+ name: 'Investment Account'
+ }
+];
+
+export default {
+ allCategories: allAccountCategories
+};
diff --git a/src/filters/currency.js b/src/filters/currency.js
index 46c58f80..0d8cc5ee 100644
--- a/src/filters/currency.js
+++ b/src/filters/currency.js
@@ -1,10 +1,11 @@
import settings from "../lib/settings.js";
+import utils from "../lib/utils.js";
export default function ({ i18n }, value, currencyCode) {
- if (!value) {
+ if (!utils.isNumber(value) && !utils.isString(value)) {
return value;
}
-
+
value = value / 100;
const currencyDisplayMode = settings.getCurrencyDisplayMode();
diff --git a/src/lib/services.js b/src/lib/services.js
index 96d77fb3..0834e2e0 100644
--- a/src/lib/services.js
+++ b/src/lib/services.js
@@ -160,4 +160,18 @@ export default {
password
});
},
+ getAllAccounts: () => {
+ return axios.get('v1/accounts/list.json');
+ },
+ addAccount: ({ category, type, name, icon, currency, comment, subAccounts }) => {
+ return axios.post('v1/accounts/add.json', {
+ category,
+ type,
+ name,
+ icon,
+ currency,
+ comment,
+ subAccounts
+ });
+ },
};
diff --git a/src/locales/en.js b/src/locales/en.js
index 0f060cd7..a1df2aeb 100644
--- a/src/locales/en.js
+++ b/src/locales/en.js
@@ -204,6 +204,11 @@ export default {
'two factor is not enabled': 'Two factor is not enabled',
'two factor has already been enabled': 'Two factor has already been enabled',
'two factor backup code does not exist': 'Two factor backup code does not exist',
+ 'account id is invalid': 'Account id is invalid',
+ 'account not found': 'Account is not found',
+ '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',
},
'parameter': {
'username': 'Username',
@@ -228,6 +233,7 @@ export default {
'OK': 'OK',
'Cancel': 'Cancel',
'Close': 'Close',
+ 'Submit': 'Submit',
'Update': 'Update',
'None': 'None',
'Done': 'Done',
@@ -286,6 +292,30 @@ export default {
'Expense': 'Expense',
'Income': 'Income',
'Transfer': 'Transfer',
+ 'Cash': 'Cash',
+ 'Debit Card': 'Debit Card',
+ 'Credit Card': 'Credit Card',
+ 'Virtual Account': 'Virtual Account',
+ 'Debt Account': 'Debt Account',
+ 'Receivables': 'Receivables',
+ 'Investment Account': 'Investment Account',
+ 'Unable to get account list': 'Unable to get account list',
+ 'Add Account': 'Add Account',
+ 'Account Category': 'Account Category',
+ 'Single Account': 'Single Account',
+ 'Multi Sub Accounts': 'Multi Sub Accounts',
+ 'Account Type': 'Account Type',
+ 'Account Name': 'Account Name',
+ 'Your account name': 'Your account name',
+ 'Currency': 'Currency',
+ 'Description': 'Description',
+ 'Your account description (optional)': 'Your account description (optional)',
+ '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',
+ 'Account currency cannot be empty': 'Account currency cannot be empty',
+ 'You have added a new account': 'You have added a new account',
+ 'Unable to add account': 'Unable to add account',
'User Profile': 'User Profile',
'Language': 'Language',
'Currency Display Mode': 'Currency Display Mode',
diff --git a/src/locales/zh_Hans.js b/src/locales/zh_Hans.js
index db1a137f..d2d8f976 100644
--- a/src/locales/zh_Hans.js
+++ b/src/locales/zh_Hans.js
@@ -204,6 +204,11 @@ export default {
'two factor is not enabled': '两步验证没有启用',
'two factor has already been enabled': '两步验证已经启用',
'two factor backup code does not exist': '两步验证备用码不存在',
+ 'account id is invalid': '账户ID无效',
+ 'account not found': '账户不存在',
+ 'account type is invalid': '账户类型无效',
+ 'account must have at least one sub account': '账户必须包含至少一个子账户',
+ 'account cannot have sub accounts': '账户不能包含子账户',
},
'parameter': {
'username': '用户名',
@@ -228,6 +233,7 @@ export default {
'OK': '确定',
'Cancel': '取消',
'Close': '关闭',
+ 'Submit': '提交',
'Update': '更新',
'None': '无',
'Done': '完成',
@@ -286,6 +292,30 @@ export default {
'Expense': '支出',
'Income': '收入',
'Transfer': '转账',
+ 'Cash': '现金',
+ 'Debit Card': '借记卡',
+ 'Credit Card': '信用卡',
+ 'Virtual Account': '虚拟账户',
+ 'Debt Account': '负债账户',
+ 'Receivables': '应收款项',
+ 'Investment Account': '投资账户',
+ 'Unable to get account list': '无法获取账户列表',
+ 'Add Account': '添加账户',
+ 'Account Category': '账户分类',
+ 'Single Account': '单一账户',
+ 'Multi Sub Accounts': '多个子账户',
+ 'Account Type': '账户类型',
+ 'Account Name': '账户名称',
+ 'Your account name': '你的账户名称',
+ 'Currency': '货币',
+ 'Description': '描述',
+ 'Your account description (optional)': '你的账户描述 (可选)',
+ 'Account category cannot be empty': '账户分类不能为空',
+ 'Account type cannot be empty': '账户类型不能为空',
+ 'Account name cannot be empty': '账户名称不能为空',
+ 'Account currency cannot be empty': '账户货币不能为空',
+ 'You have added a new account': '您已经添加新账户',
+ 'Unable to add account': '无法添加账户',
'User Profile': '用户信息',
'Language': '语言',
'Currency Display Mode': '货币显示模式',
diff --git a/src/mobile-main.js b/src/mobile-main.js
index a5c27c2d..951fa136 100644
--- a/src/mobile-main.js
+++ b/src/mobile-main.js
@@ -14,6 +14,7 @@ import 'framework7-icons';
import { getAllLanguages, getLanguage, getDefaultLanguage, getI18nOptions, getLocalizedError } from './lib/i18n.js';
import currency from './consts/currency.js';
+import account from './consts/account.js';
import version from './lib/version.js';
import settings from './lib/settings.js';
import services from './lib/services.js';
@@ -31,6 +32,9 @@ Framework7.use(Framework7Vue);
const i18n = new VueI18n(getI18nOptions());
Vue.prototype.$version = version.getVersion;
+Vue.prototype.$constants = {
+ account: account
+};
Vue.prototype.$utils = utils;
Vue.prototype.$settings = settings;
Vue.prototype.$getDefaultLanguage = getDefaultLanguage;
diff --git a/src/router/mobile.js b/src/router/mobile.js
index 5163562a..5030e9d2 100644
--- a/src/router/mobile.js
+++ b/src/router/mobile.js
@@ -8,6 +8,7 @@ import TransactionDetailPage from '../views/mobile/transactions/Detail.vue'
import TransactionNewPage from '../views/mobile/transactions/New.vue'
import AccountListPage from '../views/mobile/accounts/AccountList.vue'
+import AccountAddPage from '../views/mobile/accounts/AccountAdd.vue'
import StatisticsOverviewPage from '../views/mobile/statistics/Overview.vue'
@@ -72,6 +73,11 @@ const routes = [
component: AccountListPage,
beforeEnter: checkLogin
},
+ {
+ path: '/account/add',
+ component: AccountAddPage,
+ beforeEnter: checkLogin
+ },
{
path: '/statistic/overview',
component: StatisticsOverviewPage,
diff --git a/src/views/mobile/accounts/AccountAdd.vue b/src/views/mobile/accounts/AccountAdd.vue
new file mode 100644
index 00000000..b61d092e
--- /dev/null
+++ b/src/views/mobile/accounts/AccountAdd.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/mobile/accounts/AccountList.vue b/src/views/mobile/accounts/AccountList.vue
index f72a5da3..46099873 100644
--- a/src/views/mobile/accounts/AccountList.vue
+++ b/src/views/mobile/accounts/AccountList.vue
@@ -1,9 +1,99 @@
-
+
+
+
+
+
+
+
-
-
-
+
+ Account Category
+
+
+
+
+
+
+ {{ $t(accountCategory.name) }}
+
+
+
+
+
+