From 75d801f7759ae2430ebeff7874e53cfb8ecb8fc7 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Fri, 8 May 2026 00:58:04 +0800 Subject: [PATCH] support displaying transactions since the last reconciled time --- cmd/webserver.go | 1 + pkg/api/accounts.go | 103 ++++++++++++++++-- pkg/errs/account.go | 46 ++++---- pkg/errs/user.go | 1 + pkg/models/account.go | 6 + pkg/services/accounts.go | 21 ++++ src/core/datetime.ts | 56 ++++++---- src/lib/datetime.ts | 22 ++++ src/lib/services.ts | 4 + src/locales/de.json | 7 ++ src/locales/en.json | 7 ++ src/locales/es.json | 7 ++ src/locales/fr.json | 7 ++ src/locales/helpers.ts | 36 +++--- src/locales/it.json | 7 ++ src/locales/ja.json | 7 ++ src/locales/kn.json | 7 ++ src/locales/ko.json | 7 ++ src/locales/nl.json | 7 ++ src/locales/pt_BR.json | 7 ++ src/locales/ru.json | 7 ++ src/locales/sl.json | 7 ++ src/locales/ta.json | 7 ++ src/locales/th.json | 7 ++ src/locales/tr.json | 7 ++ src/locales/uk.json | 7 ++ src/locales/vi.json | 7 ++ src/locales/zh_Hans.json | 7 ++ src/locales/zh_Hant.json | 7 ++ src/models/account.ts | 5 + src/stores/account.ts | 37 +++++++ src/stores/transaction.ts | 7 +- .../base/accounts/AccountListPageBase.ts | 2 + .../ReconciliationStatementPageBase.ts | 26 +++++ .../statistics/StatisticsSettingPageBase.ts | 6 +- .../StatisticsTransactionPageBase.ts | 6 +- .../transactions/TransactionListPageBase.ts | 6 +- src/views/desktop/accounts/ListPage.vue | 43 +++++++- .../dialogs/ReconciliationStatementDialog.vue | 44 ++++++-- .../app/settings/tabs/AppBasicSettingTab.vue | 2 +- src/views/desktop/insights/ExplorerPage.vue | 2 +- src/views/desktop/transactions/ListPage.vue | 5 +- src/views/mobile/accounts/ListPage.vue | 35 ++++++ .../accounts/ReconciliationStatementPage.vue | 55 ++++++++-- src/views/mobile/transactions/ListPage.vue | 5 +- 45 files changed, 608 insertions(+), 107 deletions(-) diff --git a/cmd/webserver.go b/cmd/webserver.go index 230a914a..9394dfef 100644 --- a/cmd/webserver.go +++ b/cmd/webserver.go @@ -375,6 +375,7 @@ func startWebServer(c *core.CliContext) error { apiV1Route.GET("/accounts/get.json", bindApi(api.Accounts.AccountGetHandler)) apiV1Route.POST("/accounts/add.json", bindApi(api.Accounts.AccountCreateHandler)) apiV1Route.POST("/accounts/modify.json", bindApi(api.Accounts.AccountModifyHandler)) + apiV1Route.POST("/accounts/update/last_reconciled_time.json", bindApi(api.Accounts.AccountUpdateLastReconciledTimeHandler)) apiV1Route.POST("/accounts/hide.json", bindApi(api.Accounts.AccountHideHandler)) apiV1Route.POST("/accounts/move.json", bindApi(api.Accounts.AccountMoveHandler)) apiV1Route.POST("/accounts/delete.json", bindApi(api.Accounts.AccountDeleteHandler)) diff --git a/pkg/api/accounts.go b/pkg/api/accounts.go index f396711f..e83ec9ed 100644 --- a/pkg/api/accounts.go +++ b/pkg/api/accounts.go @@ -19,6 +19,7 @@ type AccountsApi struct { ApiUsingConfig ApiUsingDuplicateChecker accounts *services.AccountService + users *services.UserService } // Initialize an account api singleton instance @@ -34,6 +35,7 @@ var ( container: duplicatechecker.Container, }, accounts: services.Accounts, + users: services.Users, } ) @@ -333,6 +335,16 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error } uid := c.GetCurrentUid() + user, err := a.users.GetUserById(c, uid) + + if err != nil { + if !errs.IsCustomError(err) { + log.Errorf(c, "[accounts.AccountModifyHandler] failed to get user, because %s", err.Error()) + } + + return nil, errs.ErrUserNotFound + } + accountAndSubAccounts, err := a.accounts.GetAccountAndSubAccountsByAccountId(c, uid, accountModifyReq.Id) if err != nil { @@ -434,7 +446,11 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error var toAddAccountBalanceTimes []int64 var toDeleteAccountIds []int64 - toUpdateAccount := a.getToUpdateAccount(uid, &accountModifyReq, mainAccount, false) + toUpdateAccount, err := a.getToUpdateAccount(user, &accountModifyReq, mainAccount, false) + + if err != nil { + return nil, errs.Or(err, errs.ErrOperationFailed) + } if toUpdateAccount != nil { if toUpdateAccount.Category != mainAccount.Category { @@ -483,7 +499,11 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error toAddAccountBalanceTimes = append(toAddAccountBalanceTimes, 0) } } else { - toUpdateSubAccount := a.getToUpdateAccount(uid, subAccountReq, accountMap[subAccountReq.Id], true) + toUpdateSubAccount, err := a.getToUpdateAccount(user, subAccountReq, accountMap[subAccountReq.Id], true) + + if err != nil { + return nil, errs.Or(err, errs.ErrOperationFailed) + } if toUpdateSubAccount != nil { anythingUpdate = true @@ -607,6 +627,69 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error return accountResp, nil } +// AccountUpdateLastReconciledTimeHandler updates last reconciled time of an existed account by request parameters for current user +func (a *AccountsApi) AccountUpdateLastReconciledTimeHandler(c *core.WebContext) (any, *errs.Error) { + var accountUpdateReq models.AccountUpdateLastReconciledTimeRequest + err := c.ShouldBindJSON(&accountUpdateReq) + + if err != nil { + log.Warnf(c, "[accounts.AccountUpdateLastReconciledTimeHandler] parse request failed, because %s", err.Error()) + return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) + } + + if accountUpdateReq.Id <= 0 { + return nil, errs.ErrAccountIdInvalid + } + + uid := c.GetCurrentUid() + user, err := a.users.GetUserById(c, uid) + + if err != nil { + if !errs.IsCustomError(err) { + log.Errorf(c, "[accounts.AccountUpdateLastReconciledTimeHandler] failed to get user, because %s", err.Error()) + } + + return nil, errs.ErrUserNotFound + } + + if !user.UseLastReconciledTime { + return nil, errs.ErrLastReconciledTimeIsNotEnabled + } + + account, err := a.accounts.GetAccountByAccountId(c, uid, accountUpdateReq.Id) + + if err != nil { + log.Errorf(c, "[accounts.AccountUpdateLastReconciledTimeHandler] failed to get account \"id:%d\" for user \"uid:%d\", because %s", accountUpdateReq.Id, uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) + } + + if account.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS { + return nil, errs.ErrParentAccountCannotSetLastReconciledTime + } + + if account.Extend == nil { + account.Extend = &models.AccountExtend{} + } + + if account.Extend.LastReconciledTime != nil && accountUpdateReq.LastReconciledTime < *account.Extend.LastReconciledTime { + return nil, errs.ErrCannotSetLastReconciledTimeBeforeCurrent + } else if account.Extend.LastReconciledTime != nil && accountUpdateReq.LastReconciledTime == *account.Extend.LastReconciledTime { + return nil, errs.ErrNothingWillBeUpdated + } + + account.Extend.LastReconciledTime = &accountUpdateReq.LastReconciledTime + + err = a.accounts.UpdateAccountExtend(c, uid, account) + + if err != nil { + log.Errorf(c, "[accounts.AccountUpdateLastReconciledTimeHandler] failed to update last reconciled time for account \"id:%d\" of user \"uid:%d\", because %s", account.AccountId, uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) + } + + log.Infof(c, "[accounts.AccountUpdateLastReconciledTimeHandler] user \"uid:%d\" has updated last reconciled time \"%d\" for account \"id:%d\"", uid, account.Extend.LastReconciledTime, account.AccountId) + return true, nil +} + // AccountHideHandler hides an existed account by request parameters for current user func (a *AccountsApi) AccountHideHandler(c *core.WebContext) (any, *errs.Error) { var accountHideReq models.AccountHideRequest @@ -764,7 +847,7 @@ func (a *AccountsApi) createSubAccountModels(uid int64, accountCreateReq *models return childrenAccounts, childrenAccountBalanceTimes } -func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.AccountModifyRequest, oldAccount *models.Account, isSubAccount bool) *models.Account { +func (a *AccountsApi) getToUpdateAccount(user *models.User, accountModifyReq *models.AccountModifyRequest, oldAccount *models.Account, isSubAccount bool) (*models.Account, error) { newAccountExtend := &models.AccountExtend{} newAccountExtend.LastReconciledTime = accountModifyReq.LastReconciledTime @@ -774,7 +857,7 @@ func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.Acc newAccount := &models.Account{ AccountId: oldAccount.AccountId, - Uid: uid, + Uid: user.Uid, Name: accountModifyReq.Name, DisplayOrder: oldAccount.DisplayOrder, Category: accountModifyReq.Category, @@ -791,7 +874,7 @@ func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.Acc newAccount.Color != oldAccount.Color || newAccount.Comment != oldAccount.Comment || newAccount.Hidden != oldAccount.Hidden { - return newAccount + return newAccount, nil } oldAccountExtend := oldAccount.Extend @@ -799,16 +882,20 @@ func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.Acc if (newAccountExtend.LastReconciledTime != nil && (oldAccountExtend == nil || oldAccountExtend.LastReconciledTime == nil)) || (newAccountExtend.LastReconciledTime == nil && oldAccountExtend != nil && oldAccountExtend.LastReconciledTime != nil) || (newAccountExtend.LastReconciledTime != nil && oldAccountExtend != nil && oldAccountExtend.LastReconciledTime != nil && *newAccountExtend.LastReconciledTime != *oldAccountExtend.LastReconciledTime) { - return newAccount + if !user.UseLastReconciledTime { + return nil, errs.ErrLastReconciledTimeIsNotEnabled + } + + return newAccount, nil } if (newAccountExtend.CreditCardStatementDate != nil && (oldAccountExtend == nil || oldAccountExtend.CreditCardStatementDate == nil)) || (newAccountExtend.CreditCardStatementDate == nil && oldAccountExtend != nil && oldAccountExtend.CreditCardStatementDate != nil) || (newAccountExtend.CreditCardStatementDate != nil && oldAccountExtend != nil && oldAccountExtend.CreditCardStatementDate != nil && *newAccountExtend.CreditCardStatementDate != *oldAccountExtend.CreditCardStatementDate) { - return newAccount + return newAccount, nil } - return nil + return nil, nil } func (a *AccountsApi) getToDeleteSubAccountIds(accountModifyReq *models.AccountModifyRequest, mainAccount *models.Account, accountAndSubAccounts []*models.Account) []int64 { diff --git a/pkg/errs/account.go b/pkg/errs/account.go index abc2bb40..45adba87 100644 --- a/pkg/errs/account.go +++ b/pkg/errs/account.go @@ -4,26 +4,28 @@ import "net/http" // Error codes related to accounts var ( - ErrAccountIdInvalid = NewNormalError(NormalSubcategoryAccount, 0, http.StatusBadRequest, "account id is invalid") - ErrAccountNotFound = NewNormalError(NormalSubcategoryAccount, 1, http.StatusBadRequest, "account not found") - ErrAccountTypeInvalid = NewNormalError(NormalSubcategoryAccount, 2, http.StatusBadRequest, "account type is invalid") - ErrAccountCurrencyInvalid = NewNormalError(NormalSubcategoryAccount, 3, http.StatusBadRequest, "account currency is invalid") - ErrAccountHaveNoSubAccount = NewNormalError(NormalSubcategoryAccount, 4, http.StatusBadRequest, "account must have at least one sub-account") - ErrAccountCannotHaveSubAccounts = NewNormalError(NormalSubcategoryAccount, 5, http.StatusBadRequest, "account cannot have sub-accounts") - ErrParentAccountCannotSetCurrency = NewNormalError(NormalSubcategoryAccount, 6, http.StatusBadRequest, "parent account cannot set currency") - 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") - 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") - ErrAccountCategoryInvalid = NewNormalError(NormalSubcategoryAccount, 14, http.StatusBadRequest, "account category is invalid") - ErrAccountBalanceTimeNotSet = NewNormalError(NormalSubcategoryAccount, 15, http.StatusBadRequest, "account balance time is not set") - ErrCannotSetStatementDateForNonCreditCard = NewNormalError(NormalSubcategoryAccount, 16, http.StatusBadRequest, "cannot set statement date for non credit card account") - 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") + ErrAccountIdInvalid = NewNormalError(NormalSubcategoryAccount, 0, http.StatusBadRequest, "account id is invalid") + ErrAccountNotFound = NewNormalError(NormalSubcategoryAccount, 1, http.StatusBadRequest, "account not found") + ErrAccountTypeInvalid = NewNormalError(NormalSubcategoryAccount, 2, http.StatusBadRequest, "account type is invalid") + ErrAccountCurrencyInvalid = NewNormalError(NormalSubcategoryAccount, 3, http.StatusBadRequest, "account currency is invalid") + ErrAccountHaveNoSubAccount = NewNormalError(NormalSubcategoryAccount, 4, http.StatusBadRequest, "account must have at least one sub-account") + ErrAccountCannotHaveSubAccounts = NewNormalError(NormalSubcategoryAccount, 5, http.StatusBadRequest, "account cannot have sub-accounts") + ErrParentAccountCannotSetCurrency = NewNormalError(NormalSubcategoryAccount, 6, http.StatusBadRequest, "parent account cannot set currency") + 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") + 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") + ErrAccountCategoryInvalid = NewNormalError(NormalSubcategoryAccount, 14, http.StatusBadRequest, "account category is invalid") + ErrAccountBalanceTimeNotSet = NewNormalError(NormalSubcategoryAccount, 15, http.StatusBadRequest, "account balance time is not set") + ErrCannotSetStatementDateForNonCreditCard = NewNormalError(NormalSubcategoryAccount, 16, http.StatusBadRequest, "cannot set statement date for non credit card account") + 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") + ErrParentAccountCannotSetLastReconciledTime = NewNormalError(NormalSubcategoryAccount, 23, http.StatusBadRequest, "parent account cannot set last reconciled time") + ErrCannotSetLastReconciledTimeBeforeCurrent = NewNormalError(NormalSubcategoryAccount, 24, http.StatusBadRequest, "cannot set last reconciled time before current value") ) diff --git a/pkg/errs/user.go b/pkg/errs/user.go index b460da9a..f4a815c6 100644 --- a/pkg/errs/user.go +++ b/pkg/errs/user.go @@ -41,4 +41,5 @@ var ( ErrCannotLoginByPassword = NewNormalError(NormalSubcategoryUser, 32, http.StatusBadRequest, "cannot login by password") ErrUserNameIsInvalid = NewNormalError(NormalSubcategoryUser, 33, http.StatusBadRequest, "user name is invalid") ErrNickNameIsInvalid = NewNormalError(NormalSubcategoryUser, 34, http.StatusBadRequest, "nick name is invalid") + ErrLastReconciledTimeIsNotEnabled = NewNormalError(NormalSubcategoryUser, 35, http.StatusBadRequest, "last reconciled time is not enabled") ) diff --git a/pkg/models/account.go b/pkg/models/account.go index 61ecc52d..6958e57d 100644 --- a/pkg/models/account.go +++ b/pkg/models/account.go @@ -128,6 +128,12 @@ type AccountModifyRequest struct { ClientSessionId string `json:"clientSessionId"` } +// AccountUpdateLastReconciledTimeRequest represents all parameters of account updating last reconciled time request +type AccountUpdateLastReconciledTimeRequest struct { + Id int64 `json:"id,string" binding:"required,min=1"` + LastReconciledTime int64 `json:"lastReconciledTime" binding:"required"` +} + // AccountListRequest represents all parameters of account listing request type AccountListRequest struct { VisibleOnly bool `form:"visible_only"` diff --git a/pkg/services/accounts.go b/pkg/services/accounts.go index 25441a27..73d5f9d9 100644 --- a/pkg/services/accounts.go +++ b/pkg/services/accounts.go @@ -592,6 +592,27 @@ func (s *AccountService) ModifyAccounts(c core.Context, mainAccount *models.Acco }) } +// UpdateAccountExtend updates extend field of given account +func (s *AccountService) UpdateAccountExtend(c core.Context, uid int64, account *models.Account) error { + if uid <= 0 { + return errs.ErrUserIdInvalid + } + + account.UpdatedUnixTime = time.Now().Unix() + + return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error { + updatedRows, err := sess.ID(account.AccountId).Cols("extend", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(account) + + if err != nil { + return err + } else if updatedRows < 1 { + return errs.ErrAccountNotFound + } + + return nil + }) +} + // HideAccount updates hidden field of given accounts func (s *AccountService) HideAccount(c core.Context, uid int64, ids []int64, hidden bool) error { if uid <= 0 { diff --git a/src/core/datetime.ts b/src/core/datetime.ts index 5a0fa92c..26a9350f 100644 --- a/src/core/datetime.ts +++ b/src/core/datetime.ts @@ -701,49 +701,54 @@ export class DateRange implements TypeAndName { private static readonly allInstancesByType: Record = {}; // All date range - public static readonly All = new DateRange(0, 'All', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly All = new DateRange(0, 'All', false, false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); // Date ranges for normal scene only - public static readonly Today = new DateRange(1, 'Today', false, false, DateRangeScene.Normal, DateRangeScene.InsightsExplorer); - public static readonly Yesterday = new DateRange(2, 'Yesterday', false, false, DateRangeScene.Normal, DateRangeScene.InsightsExplorer); - public static readonly LastSevenDays = new DateRange(3, 'Recent 7 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly LastThirtyDays = new DateRange(4, 'Recent 30 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly ThisWeek = new DateRange(5, 'This week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly LastWeek = new DateRange(6, 'Last week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly ThisMonth = new DateRange(7, 'This month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly LastMonth = new DateRange(8, 'Last month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly Today = new DateRange(1, 'Today', false, false, false, DateRangeScene.Normal, DateRangeScene.InsightsExplorer); + public static readonly Yesterday = new DateRange(2, 'Yesterday', false, false, false, DateRangeScene.Normal, DateRangeScene.InsightsExplorer); + public static readonly LastSevenDays = new DateRange(3, 'Recent 7 days', false, false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly LastThirtyDays = new DateRange(4, 'Recent 30 days', false, false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly ThisWeek = new DateRange(5, 'This week', false, false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly LastWeek = new DateRange(6, 'Last week', false, false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly ThisMonth = new DateRange(7, 'This month', false, false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly LastMonth = new DateRange(8, 'Last month', false, false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); // Date ranges for normal and trend analysis scene - public static readonly ThisYear = new DateRange(9, 'This year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly LastYear = new DateRange(10, 'Last year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly ThisFiscalYear = new DateRange(11, 'This fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly LastFiscalYear = new DateRange(12, 'Last fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly ThisYear = new DateRange(9, 'This year', false, false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly LastYear = new DateRange(10, 'Last year', false, false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly ThisFiscalYear = new DateRange(11, 'This fiscal year', false, false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly LastFiscalYear = new DateRange(12, 'Last fiscal year', false, false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); // Billing cycle date ranges for normal scene only - public static readonly CurrentBillingCycle = new DateRange(51, 'Current Billing Cycle', true, true, DateRangeScene.Normal); - public static readonly PreviousBillingCycle = new DateRange(52, 'Previous Billing Cycle', true, true, DateRangeScene.Normal); + public static readonly CurrentBillingCycle = new DateRange(51, 'Current Billing Cycle', true, false, true, DateRangeScene.Normal); + public static readonly PreviousBillingCycle = new DateRange(52, 'Previous Billing Cycle', true, false, true, DateRangeScene.Normal); + + // Last reconciled time ranges for normal scene only + public static readonly SinceLastReconciledTime = new DateRange(71, 'Since Last Reconciled Time', false, true, true, DateRangeScene.Normal) // Date ranges for trend analysis scene only - public static readonly RecentTwelveMonths = new DateRange(101, 'Recent 12 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly RecentTwentyFourMonths = new DateRange(102, 'Recent 24 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly RecentThirtySixMonths = new DateRange(103, 'Recent 36 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly RecentTwoYears = new DateRange(104, 'Recent 2 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly RecentThreeYears = new DateRange(105, 'Recent 3 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); - public static readonly RecentFiveYears = new DateRange(106, 'Recent 5 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly RecentTwelveMonths = new DateRange(101, 'Recent 12 months', false, false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly RecentTwentyFourMonths = new DateRange(102, 'Recent 24 months', false, false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly RecentThirtySixMonths = new DateRange(103, 'Recent 36 months', false, false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly RecentTwoYears = new DateRange(104, 'Recent 2 years', false, false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly RecentThreeYears = new DateRange(105, 'Recent 3 years', false, false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly RecentFiveYears = new DateRange(106, 'Recent 5 years', false, false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); // Custom date range - public static readonly Custom = new DateRange(255, 'Custom Date', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); + public static readonly Custom = new DateRange(255, 'Custom Date', false, false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends, DateRangeScene.InsightsExplorer); public readonly type: number; public readonly name: string; public readonly isBillingCycle: boolean; + public readonly isLastReconciledTimeRange: boolean; public readonly isUserCustomRange: boolean; private readonly availableScenes: Record; - private constructor(type: number, name: string, isBillingCycle: boolean, isUserCustomRange: boolean, ...availableScenes: DateRangeScene[]) { + private constructor(type: number, name: string, isBillingCycle: boolean, isLastReconciledTimeRange: boolean, isUserCustomRange: boolean, ...availableScenes: DateRangeScene[]) { this.type = type; this.name = name; this.isBillingCycle = isBillingCycle; + this.isLastReconciledTimeRange = isLastReconciledTimeRange; this.isUserCustomRange = isUserCustomRange; this.availableScenes = {}; @@ -778,4 +783,9 @@ export class DateRange implements TypeAndName { const dateRange = DateRange.allInstancesByType[type]; return dateRange?.isBillingCycle || false; } + + public static isLastReconciledTimeRange(type: number): boolean { + const dateRange = DateRange.allInstancesByType[type]; + return dateRange?.isLastReconciledTimeRange || false; + } } diff --git a/src/lib/datetime.ts b/src/lib/datetime.ts index 8851cd50..ba61864b 100644 --- a/src/lib/datetime.ts +++ b/src/lib/datetime.ts @@ -1433,6 +1433,28 @@ export function getDateRangeByBillingCycleDateType(dateType: number, firstDayOfW }; } +export function getDateRangeByLastReconciledTimeRangeDateType(dateType: number, lastReconciledTime: number | undefined | null): TimeRangeAndDateType | null { + let maxTime = 0; + let minTime = 0; + + if (dateType === DateRange.SinceLastReconciledTime.type) { // Since Last Reconciled Time + if (lastReconciledTime) { + maxTime = getTodayLastUnixTime(); + minTime = lastReconciledTime; + } else { + return null; + } + } else { + return null; + } + + return { + dateType: dateType, + maxTime: maxTime, + minTime: minTime + }; +} + export function getRecentMonthDateRanges(monthCount: number): RecentMonthDateRange[] { const recentDateRanges: RecentMonthDateRange[] = []; const thisMonthFirstUnixTime = getThisMonthFirstUnixTime(); diff --git a/src/lib/services.ts b/src/lib/services.ts index e47f540d..141a6d37 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -35,6 +35,7 @@ import { import type { AccountCreateRequest, AccountModifyRequest, + AccountUpdateLastReconciledTimeRequest, AccountInfoResponse, AccountHideRequest, AccountMoveRequest, @@ -506,6 +507,9 @@ export default { modifyAccount: (req: AccountModifyRequest): ApiResponsePromise => { return axios.post>('v1/accounts/modify.json', req); }, + updateAccountLastReconciledTime: (req: AccountUpdateLastReconciledTimeRequest): ApiResponsePromise => { + return axios.post>('v1/accounts/update/last_reconciled_time.json', req); + }, hideAccount: (req: AccountHideRequest): ApiResponsePromise => { return axios.post>('v1/accounts/hide.json', req); }, diff --git a/src/locales/de.json b/src/locales/de.json index fd86ba7a..05130f85 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1112,6 +1112,7 @@ "cannot login by password": "Sie können sich nicht mit einem Passwort anmelden", "user name is invalid": "Benutzername ist ungültig", "nick name is invalid": "Benutzerspitzname ist ungültig", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Unbefugter Zugriff", "current token is invalid": "Aktuelles Token ist ungültig", "current token is expired": "Aktuelles Token ist abgelaufen", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Änderung der Kontowährung wird nicht unterstützt", "not supported to modify account balance": "Änderung des Kontosaldos wird nicht unterstützt", "not supported to modify account balance time": "Änderung der Kontosaldozeit wird nicht unterstützt", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "Transaktions-ID ist ungültig", "transaction not found": "Transaktion nicht gefunden", "transaction type is invalid": "Transaktionstyp ist ungültig", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Letzte 5 Jahre", "Previous Billing Cycle": "Vorheriger Abrechnungszeitraum", "Current Billing Cycle": "Aktueller Abrechnungszeitraum", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Benutzerdefiniertes Datum", @@ -1941,6 +1945,9 @@ "You have saved this account": "Sie haben dieses Konto gespeichert", "Unable to add account": "Konto kann nicht hinzugefügt werden", "Unable to save account": "Konto kann nicht gespeichert werden", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Versteckte Konten anzeigen", "Hide Hidden Accounts": "Versteckte Konten ausblenden", "Set Accounts Included in Total": "Konten in Gesamtsumme einbeziehen", diff --git a/src/locales/en.json b/src/locales/en.json index fd19c4dd..6270566c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Unauthorized access", "current token is invalid": "Current token is invalid", "current token is expired": "Current token is expired", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Not supported to modify account currency", "not supported to modify account balance": "Not supported to modify account balance", "not supported to modify account balance time": "Not supported to modify account balance time", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "Transaction ID is invalid", "transaction not found": "Transaction is not found", "transaction type is invalid": "Transaction type is invalid", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Recent 5 years", "Previous Billing Cycle": "Previous Billing Cycle", "Current Billing Cycle": "Current Billing Cycle", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Custom Date", @@ -1941,6 +1945,9 @@ "You have saved this account": "You have saved this account", "Unable to add account": "Unable to add account", "Unable to save account": "Unable to save account", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Show Hidden Accounts", "Hide Hidden Accounts": "Hide Hidden Accounts", "Set Accounts Included in Total": "Set Accounts Included in Total", diff --git a/src/locales/es.json b/src/locales/es.json index e1235ac1..da00b8ac 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1112,6 +1112,7 @@ "cannot login by password": "No puede iniciar sesión con contraseña.", "user name is invalid": "El nombre de usuario no es válido.", "nick name is invalid": "El apodo de usuario no es válido.", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Acceso no autorizado", "current token is invalid": "El token actual no es válido", "current token is expired": "El token actual ha caducado", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "No se admite la modificación de la moneda de la cuenta", "not supported to modify account balance": "No se admite la modificación del saldo de la cuenta", "not supported to modify account balance time": "No se admite la modificación del tiempo de saldo de la cuenta", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "El ID de transacción no es válido", "transaction not found": "La transacción no se encuentra", "transaction type is invalid": "El tipo de transacción no es válido", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Últimos 5 años", "Previous Billing Cycle": "Ciclo de facturación anterior", "Current Billing Cycle": "Ciclo de facturación actual", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Fecha personalizada", @@ -1941,6 +1945,9 @@ "You have saved this account": "Has guardado esta cuenta", "Unable to add account": "No se puede agregar la cuenta", "Unable to save account": "No se puede guardar la cuenta", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Mostrar Cuentas Ocultas", "Hide Hidden Accounts": "Ocultar Cuentas Ocultas", "Set Accounts Included in Total": "Cuentas Incluidas en el Total", diff --git a/src/locales/fr.json b/src/locales/fr.json index d6b1acff..da802ad9 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Accès non autorisé", "current token is invalid": "Le token actuel est invalide", "current token is expired": "Le token actuel a expiré", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Modification de la devise du compte non prise en charge", "not supported to modify account balance": "Modification du solde du compte non prise en charge", "not supported to modify account balance time": "Modification de l'heure du solde du compte non prise en charge", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "L'ID de transaction est invalide", "transaction not found": "Transaction non trouvée", "transaction type is invalid": "Le type de transaction est invalide", @@ -1548,6 +1551,7 @@ "Recent 5 years": "5 dernières années", "Previous Billing Cycle": "Cycle de facturation précédent", "Current Billing Cycle": "Cycle de facturation actuel", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Date personnalisée", @@ -1941,6 +1945,9 @@ "You have saved this account": "Vous avez enregistré ce compte", "Unable to add account": "Impossible d'ajouter le compte", "Unable to save account": "Impossible d'enregistrer le compte", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Afficher les comptes masqués", "Hide Hidden Accounts": "Masquer les comptes masqués", "Set Accounts Included in Total": "Définir les comptes inclus dans le total", diff --git a/src/locales/helpers.ts b/src/locales/helpers.ts index 9256c670..706b8849 100644 --- a/src/locales/helpers.ts +++ b/src/locales/helpers.ts @@ -1172,36 +1172,26 @@ export function useI18n() { return ret; } - function getAllDateRanges(scene: DateRangeScene, includeCustom?: boolean, includeBillingCycle?: boolean): LocalizedDateRange[] { + function getAllDateRanges(scene: DateRangeScene, { includeCustom, includeBillingCycle, includeLastReconciledTimeRange } : { includeCustom?: boolean, includeBillingCycle?: boolean, includeLastReconciledTimeRange?: boolean }): LocalizedDateRange[] { const ret: LocalizedDateRange[] = []; const allDateRanges = DateRange.values(); for (const dateRange of allDateRanges) { - if (!dateRange.isAvailableForScene(scene)) { + const shouldSkip: boolean = !dateRange.isAvailableForScene(scene) + || (dateRange.isBillingCycle && !includeBillingCycle) + || (dateRange.isLastReconciledTimeRange && !includeLastReconciledTimeRange) + || (dateRange.type === DateRange.Custom.type && !includeCustom); + + if (shouldSkip) { continue; } - if (dateRange.isBillingCycle) { - if (includeBillingCycle) { - ret.push({ - type: dateRange.type, - displayName: t(dateRange.name), - isBillingCycle: dateRange.isBillingCycle, - isUserCustomRange: dateRange.isUserCustomRange - }); - } - - continue; - } - - if (includeCustom || dateRange.type !== DateRange.Custom.type) { - ret.push({ - type: dateRange.type, - displayName: t(dateRange.name), - isBillingCycle: dateRange.isBillingCycle, - isUserCustomRange: dateRange.isUserCustomRange - }); - } + ret.push({ + type: dateRange.type, + displayName: t(dateRange.name), + isBillingCycle: dateRange.isBillingCycle, + isUserCustomRange: dateRange.isUserCustomRange + }); } return ret; diff --git a/src/locales/it.json b/src/locales/it.json index 893d770b..7b37a900 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Accesso non autorizzato", "current token is invalid": "Token corrente non valido", "current token is expired": "Token corrente scaduto", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Not supported to modify account currency", "not supported to modify account balance": "Not supported to modify account balance", "not supported to modify account balance time": "Not supported to modify account balance time", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "ID transazione non valido", "transaction not found": "Transazione non trovata", "transaction type is invalid": "Tipo di transazione non valido", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Ultimi 5 anni", "Previous Billing Cycle": "Ciclo di fatturazione precedente", "Current Billing Cycle": "Ciclo di fatturazione corrente", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Data personalizzata", @@ -1941,6 +1945,9 @@ "You have saved this account": "Hai salvato questo account", "Unable to add account": "Impossibile aggiungere l'account", "Unable to save account": "Impossibile salvare l'account", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Mostra account nascosti", "Hide Hidden Accounts": "Nascondi account nascosti", "Set Accounts Included in Total": "Set Accounts Included in Total", diff --git a/src/locales/ja.json b/src/locales/ja.json index e80af7b0..088d5b3b 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "不正アクセス", "current token is invalid": "現在のトークンは無効です", "current token is expired": "現在のトークンの有効期限が切れています", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Not supported to modify account currency", "not supported to modify account balance": "Not supported to modify account balance", "not supported to modify account balance time": "Not supported to modify account balance time", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "取引IDは無効です", "transaction not found": "取引が見つかりません", "transaction type is invalid": "取引タイプは無効です", @@ -1548,6 +1551,7 @@ "Recent 5 years": "直近5年", "Previous Billing Cycle": "以前の請求サイクル", "Current Billing Cycle": "現在の請求サイクル", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "カスタム日付", @@ -1941,6 +1945,9 @@ "You have saved this account": "この口座を保存しました", "Unable to add account": "口座を追加できません", "Unable to save account": "口座を保存できません", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "非表示口座を表示します", "Hide Hidden Accounts": "非表示口座を隠します", "Set Accounts Included in Total": "Set Accounts Included in Total", diff --git a/src/locales/kn.json b/src/locales/kn.json index 8f47cc94..b700996f 100644 --- a/src/locales/kn.json +++ b/src/locales/kn.json @@ -1112,6 +1112,7 @@ "cannot login by password": "ಪಾಸ್‌ವರ್ಡ್ ಮೂಲಕ ಲಾಗಿನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ", "user name is invalid": "ಬಳಕೆದಾರ ಹೆಸರು ಅಮಾನ್ಯವಾಗಿದೆ", "nick name is invalid": "ಉಪನಾಮ ಅಮಾನ್ಯವಾಗಿದೆ", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "ಅನಧಿಕೃತ ಪ್ರವೇಶ", "current token is invalid": "ಪ್ರಸ್ತುತ ಟೋಕನ್ ಅಮಾನ್ಯವಾಗಿದೆ", "current token is expired": "ಪ್ರಸ್ತುತ ಟೋಕನ್ ಅವಧಿ ಮೀರಿದೆ", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "ಖಾತೆ ಕರೆನ್ಸಿಯನ್ನು ಬದಲಿಸಲು ಬೆಂಬಲಿಸಲಾಗುವುದಿಲ್ಲ", "not supported to modify account balance": "ಖಾತೆ ಬ್ಯಾಲೆನ್ಸ್ ಬದಲಾವಣೆ ಬೆಂಬಲಿಸಲಾಗುವುದಿಲ್ಲ", "not supported to modify account balance time": "ಖಾತೆ ಬ್ಯಾಲೆನ್ಸ್ ಸಮಯ ಬದಲಾವಣೆ ಬೆಂಬಲಿಸಲಾಗುವುದಿಲ್ಲ", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "ವಹಿವಾಟು ID ಅಮಾನ್ಯವಾಗಿದೆ", "transaction not found": "ವಹಿವಾಟು ಸಿಕ್ಕಿಲ್ಲ", "transaction type is invalid": "ವಹಿವಾಟಿನ ಪ್ರಕಾರ ಅಮಾನ್ಯವಾಗಿದೆ", @@ -1548,6 +1551,7 @@ "Recent 5 years": "ಇತ್ತೀಚಿನ 5 ವರ್ಷಗಳು", "Previous Billing Cycle": "ಹಿಂದಿನ ಬಿಲ್ಲಿಂಗ್ ಚಕ್ರ", "Current Billing Cycle": "ಪ್ರಸ್ತುತ ಬಿಲ್ಲಿಂಗ್ ಚಕ್ರ", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "ಕಸ್ಟಮ್ ದಿನಾಂಕ", @@ -1941,6 +1945,9 @@ "You have saved this account": "ನೀವು ಈ ಖಾತೆಯನ್ನು ಉಳಿಸಿದ್ದೀರಿ", "Unable to add account": "ಖಾತೆ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ", "Unable to save account": "ಖಾತೆ ಉಳಿಸಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "ಮರೆಮಾಡಿದ ಖಾತೆಗಳನ್ನು ತೋರಿಸಿ", "Hide Hidden Accounts": "ಮರೆಮಾಡಿದ ಖಾತೆಗಳನ್ನು ಅಡಗಿಸಿ", "Set Accounts Included in Total": "ಒಟ್ಟು ಲೆಕ್ಕದಲ್ಲಿ ಸೇರಿಸಲಾದ ಖಾತೆಗಳನ್ನು ಸೆಟ್ ಮಾಡಿ", diff --git a/src/locales/ko.json b/src/locales/ko.json index d4780ede..d380529e 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1112,6 +1112,7 @@ "cannot login by password": "비밀번호로 로그인할 수 없습니다", "user name is invalid": "사용자 이름이 유효하지 않습니다", "nick name is invalid": "사용자 닉네임이 유효하지 않습니다", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "권한이 없는 접근입니다", "current token is invalid": "현재 토큰이 유효하지 않습니다", "current token is expired": "현재 토큰이 만료되었습니다", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "계좌 통화를 수정하는 것은 지원되지 않습니다", "not supported to modify account balance": "계좌 잔액을 수정하는 것은 지원되지 않습니다", "not supported to modify account balance time": "계좌 잔액 시간을 수정하는 것은 지원되지 않습니다", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "거래 ID가 유효하지 않습니다", "transaction not found": "거래를 찾을 수 없습니다", "transaction type is invalid": "거래 유형이 유효하지 않습니다", @@ -1548,6 +1551,7 @@ "Recent 5 years": "최근 5년", "Previous Billing Cycle": "이전 청구 주기", "Current Billing Cycle": "현재 청구 주기", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "사용자 지정 날짜", @@ -1941,6 +1945,9 @@ "You have saved this account": "이 계좌가 저장되었습니다.", "Unable to add account": "계좌를 추가할 수 없습니다.", "Unable to save account": "계좌를 저장할 수 없습니다.", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "숨겨진 계좌 표시", "Hide Hidden Accounts": "숨겨진 계좌 숨기기", "Set Accounts Included in Total": "총계에 포함된 계좌 설정", diff --git a/src/locales/nl.json b/src/locales/nl.json index a6603a6d..be283792 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Ongeautoriseerde toegang", "current token is invalid": "Huidige token is ongeldig", "current token is expired": "Huidige token is verlopen", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Wijzigen van rekeningvaluta wordt niet ondersteund", "not supported to modify account balance": "Wijzigen van rekeningsaldo wordt niet ondersteund", "not supported to modify account balance time": "Wijzigen van tijdstip rekeningsaldo wordt niet ondersteund", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "Transactie-ID is ongeldig", "transaction not found": "Transactie niet gevonden", "transaction type is invalid": "Transactietype is ongeldig", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Afgelopen 5 jaar", "Previous Billing Cycle": "Vorige factureringsperiode", "Current Billing Cycle": "Huidige factureringsperiode", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Aangepaste datum", @@ -1941,6 +1945,9 @@ "You have saved this account": "Je hebt deze rekening opgeslagen", "Unable to add account": "Kan rekening niet toevoegen", "Unable to save account": "Kan rekening niet opslaan", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Verborgen rekeningen tonen", "Hide Hidden Accounts": "Verborgen rekeningen verbergen", "Set Accounts Included in Total": "Rekeningen kiezen voor totaal", diff --git a/src/locales/pt_BR.json b/src/locales/pt_BR.json index 4fe370e6..bc700573 100644 --- a/src/locales/pt_BR.json +++ b/src/locales/pt_BR.json @@ -1112,6 +1112,7 @@ "cannot login by password": "Você não pode entrar com senha", "user name is invalid": "Nome de usuário é inválido", "nick name is invalid": "Nome de exibição é inválido", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Acesso não autorizado", "current token is invalid": "Token atual é inválido", "current token is expired": "Token atual está expirado", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Não é suportado modificar moeda da conta", "not supported to modify account balance": "Não é suportado modificar saldo da conta", "not supported to modify account balance time": "Não é suportado modificar tempo de saldo da conta", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "ID da transação é inválido", "transaction not found": "Transação não foi encontrada", "transaction type is invalid": "Tipo de transação é inválido", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Últimos 5 anos", "Previous Billing Cycle": "Ciclo de Cobrança Anterior", "Current Billing Cycle": "Ciclo de Cobrança Atual", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Data Personalizada", @@ -1941,6 +1945,9 @@ "You have saved this account": "Você salvou esta conta", "Unable to add account": "Não foi possível adicionar conta", "Unable to save account": "Não foi possível salvar conta", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Mostrar Contas Ocultas", "Hide Hidden Accounts": "Ocultar Contas Ocultas", "Set Accounts Included in Total": "Definir Contas Incluídas no Total", diff --git a/src/locales/ru.json b/src/locales/ru.json index 651e7ce6..d08405ae 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1112,6 +1112,7 @@ "cannot login by password": "Вы не можете войти с помощью пароля", "user name is invalid": "Недействительное имя пользователя", "nick name is invalid": "Недействительный ник пользователя", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Несанкционированный доступ", "current token is invalid": "Текущий токен недействителен", "current token is expired": "Текущий токен истек", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Изменение валюты счёта не поддерживается", "not supported to modify account balance": "Изменение баланса счёта не поддерживается", "not supported to modify account balance time": "Изменение времени баланса счёта не поддерживается", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "ID транзакции недействителен", "transaction not found": "Транзакция не найдена", "transaction type is invalid": "Тип транзакции недействителен", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Последние 5 лет", "Previous Billing Cycle": "Предыдущий расчетный период", "Current Billing Cycle": "Текущий расчетный период", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Выбрать дату", @@ -1941,6 +1945,9 @@ "You have saved this account": "Вы сохранили этот счет", "Unable to add account": "Не удалось добавить счет", "Unable to save account": "Не удалось сохранить счет", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Показать скрытые счета", "Hide Hidden Accounts": "Скрыть скрытые счета", "Set Accounts Included in Total": "Включить счет в итого", diff --git a/src/locales/sl.json b/src/locales/sl.json index 47fdb040..68b115e1 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -1112,6 +1112,7 @@ "cannot login by password": "Prijava z geslom ni mogoča", "user name is invalid": "Uporabniško ime ni veljavno", "nick name is invalid": "Vzdevek uporabnika ni veljaven", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Nepooblaščen dostop", "current token is invalid": "Trenutni žeton ni veljaven", "current token is expired": "Trenutni žeton je potekel", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Spreminjanje valute računa ni podprto", "not supported to modify account balance": "Spreminjanje stanja računa ni podprto", "not supported to modify account balance time": "Spreminjanje časa stanja na računu ni podprto", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "ID transakcije ni veljaven", "transaction not found": "Transakcije ni mogoče najti", "transaction type is invalid": "Vrsta transakcije ni veljavna", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Zadnjih 5 let", "Previous Billing Cycle": "Prejšnje obračunsko obdobje", "Current Billing Cycle": "Trenutno obračunsko obdobje", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Datum po meri", @@ -1941,6 +1945,9 @@ "You have saved this account": "Ta račun ste shranili", "Unable to add account": "Računa ni mogoče dodati", "Unable to save account": "Računa ni mogoče shraniti", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Prikaži skrite račune", "Hide Hidden Accounts": "Skrij skrite račune", "Set Accounts Included in Total": "Nastavi račune, vključene v skupno vsoto", diff --git a/src/locales/ta.json b/src/locales/ta.json index a7ce730f..7a54d145 100644 --- a/src/locales/ta.json +++ b/src/locales/ta.json @@ -1112,6 +1112,7 @@ "cannot login by password": "கடவுச்சொல் மூலம் உள்நுழைய முடியாது", "user name is invalid": "பயனர் பெயர் தவறானது உள்ளது", "nick name is invalid": "புனைப்பெயர் தவறானது உள்ளது", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "அங்கீகரிக்கப்படாத அணுகல்", "current token is invalid": "தற்போதைய டோக்கன் தவறானது உள்ளது", "current token is expired": "தற்போதைய டோக்கன் காலாவதியானது", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "கணக்கு நாணயம்யை மாற்ற ஆதரிக்கப்படவில்லை", "not supported to modify account balance": "கணக்கு இருப்பு மாற்றம் ஆதரிக்கப்படவில்லை", "not supported to modify account balance time": "கணக்கு இருப்பு நேரம் மாற்றம் ஆதரிக்கப்படவில்லை", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "பரிவர்த்தனை ID தவறானது உள்ளது", "transaction not found": "பரிவர்த்தனை கிடைக்கவில்லை", "transaction type is invalid": "பரிவர்த்தனையின் வகை தவறானது உள்ளது", @@ -1548,6 +1551,7 @@ "Recent 5 years": "சமீபத்திய 5 ஆண்டுகள்", "Previous Billing Cycle": "முந்தைய பில்லிங் சுழற்சி", "Current Billing Cycle": "தற்போதைய பில்லிங் சுழற்சி", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "தனிப்பயன் தேதி", @@ -1941,6 +1945,9 @@ "You have saved this account": "நீங்கள் இந்த கணக்கை சேமித்தீர்கள்", "Unable to add account": "கணக்கு சேர்க்க முடியவில்லை", "Unable to save account": "கணக்கு சேமிக்க முடியவில்லை", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "மறைந்த கணக்குகளை காட்டு", "Hide Hidden Accounts": "மறைந்த கணக்குகளை மறை", "Set Accounts Included in Total": "மொத்தம் லெக்கத்தில் சேர்க்கப்பட்ட கணக்குகளை அமை செய்", diff --git a/src/locales/th.json b/src/locales/th.json index 9b589cf3..312bacec 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "การเข้าถึงไม่ได้รับอนุญาต", "current token is invalid": "โทเค็นปัจจุบันไม่ถูกต้อง", "current token is expired": "โทเค็นปัจจุบันหมดอายุ", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "ไม่รองรับการแก้ไขสกุลเงินบัญชี", "not supported to modify account balance": "ไม่รองรับการแก้ไขยอดเงินบัญชี", "not supported to modify account balance time": "ไม่รองรับการแก้ไขเวลายอดเงินบัญชี", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "รหัสธุรกรรมไม่ถูกต้อง", "transaction not found": "ไม่พบธุรกรรม", "transaction type is invalid": "ประเภทธุรกรรมไม่ถูกต้อง", @@ -1548,6 +1551,7 @@ "Recent 5 years": "5 ปีที่ผ่านมา", "Previous Billing Cycle": "รอบบิลก่อนหน้า", "Current Billing Cycle": "รอบบิลปัจจุบัน", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "วันที่กำหนดเอง", @@ -1941,6 +1945,9 @@ "You have saved this account": "คุณได้บันทึกบัญชีนี้แล้ว", "Unable to add account": "ไม่สามารถเพิ่มบัญชีได้", "Unable to save account": "ไม่สามารถบันทึกบัญชีได้", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "แสดงบัญชีที่ซ่อนอยู่", "Hide Hidden Accounts": "ซ่อนบัญชีที่ซ่อนอยู่", "Set Accounts Included in Total": "ตั้งค่าบัญชีที่รวมในยอดรวม", diff --git a/src/locales/tr.json b/src/locales/tr.json index ebff9fc0..317f5cca 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -1112,6 +1112,7 @@ "cannot login by password": "Şifre ile giriş yapamazsınız", "user name is invalid": "Kullanıcı adı geçersiz", "nick name is invalid": "Takma ad geçersiz", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Yetkisiz erişim", "current token is invalid": "Mevcut jeton (token) geçersiz", "current token is expired": "Mevcut jetonun (token) süresi dolmuş", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Hesap para birimini değiştirmek desteklenmiyor", "not supported to modify account balance": "Hesap bakiyesini değiştirmek desteklenmiyor", "not supported to modify account balance time": "Hesap bakiye zamanını değiştirmek desteklenmiyor", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "İşlem ID geçersiz", "transaction not found": "İşlem bulunamadı", "transaction type is invalid": "İşlem türü geçersiz", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Son 5 yıl", "Previous Billing Cycle": "Önceki Fatura Dönemi", "Current Billing Cycle": "Mevcut Fatura Dönemi", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Özel Tarih", @@ -1941,6 +1945,9 @@ "You have saved this account": "Bu hesabı kaydettiniz", "Unable to add account": "Hesap eklenemedi", "Unable to save account": "Hesap kaydedilemedi", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Gizli Hesapları Göster", "Hide Hidden Accounts": "Gizli Hesapları Gizle", "Set Accounts Included in Total": "Toplamda Dahil Edilecek Hesapları Ayarla", diff --git a/src/locales/uk.json b/src/locales/uk.json index 669ef29f..0fe85f03 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Несанкціонований доступ", "current token is invalid": "Поточний токен недійсний", "current token is expired": "Поточний токен прострочений", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Not supported to modify account currency", "not supported to modify account balance": "Not supported to modify account balance", "not supported to modify account balance time": "Not supported to modify account balance time", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "ID транзакції недійсний", "transaction not found": "Транзакцію не знайдено", "transaction type is invalid": "Тип транзакції недійсний", @@ -1548,6 +1551,7 @@ "Recent 5 years": "Останні 5 років", "Previous Billing Cycle": "Попередній розрахунковий період", "Current Billing Cycle": "Поточний розрахунковий період", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Обрати дату", @@ -1941,6 +1945,9 @@ "You have saved this account": "Ви зберегли цей рахунок", "Unable to add account": "Не вдалося додати рахунок", "Unable to save account": "Не вдалося зберегти рахунок", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Показати приховані рахунки", "Hide Hidden Accounts": "Приховати приховані рахунки", "Set Accounts Included in Total": "Set Accounts Included in Total", diff --git a/src/locales/vi.json b/src/locales/vi.json index fb254561..cd76ca3a 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1112,6 +1112,7 @@ "cannot login by password": "You cannot login by password", "user name is invalid": "User name is invalid", "nick name is invalid": "User nickname is invalid", + "last reconciled time is not enabled": "Last reconciled time is not enabled", "unauthorized access": "Truy cập trái phép", "current token is invalid": "Mã thông báo hiện tại không hợp lệ", "current token is expired": "Mã thông báo hiện tại đã hết hạn", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "Not supported to modify account currency", "not supported to modify account balance": "Not supported to modify account balance", "not supported to modify account balance time": "Not supported to modify account balance time", + "parent account cannot set last reconciled time": "Parent account cannot set last reconciled time", + "cannot set last reconciled time before current value": "Cannot set last reconciled time before current value", "transaction id is invalid": "ID giao dịch không hợp lệ", "transaction not found": "Không tìm thấy giao dịch", "transaction type is invalid": "Loại giao dịch không hợp lệ", @@ -1548,6 +1551,7 @@ "Recent 5 years": "5 năm gần đây", "Previous Billing Cycle": "Previous Billing Cycle", "Current Billing Cycle": "Current Billing Cycle", + "Since Last Reconciled Time": "Since Last Reconciled Time", "Last day": "Last day", "last day": "last day", "Custom Date": "Ngày tùy chỉnh", @@ -1941,6 +1945,9 @@ "You have saved this account": "Bạn đã lưu tài khoản này", "Unable to add account": "Không thể thêm tài khoản", "Unable to save account": "Không thể lưu tài khoản", + "Mark as Reconciled": "Mark as Reconciled", + "Last reconciled time have been updated": "Last reconciled time have been updated", + "Unable to update last reconciled time": "Unable to update last reconciled time", "Show Hidden Accounts": "Hiển thị tài khoản ẩn", "Hide Hidden Accounts": "Ẩn tài khoản ẩn", "Set Accounts Included in Total": "Set Accounts Included in Total", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index 1384776f..e075fecc 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -1112,6 +1112,7 @@ "cannot login by password": "您不能使用密码登录", "user name is invalid": "用户名无效", "nick name is invalid": "用户昵称无效", + "last reconciled time is not enabled": "最后对账时间没有启用", "unauthorized access": "未授权的登录", "current token is invalid": "当前认证令牌无效", "current token is expired": "当前认证令牌已过期", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "不支持修改账户货币", "not supported to modify account balance": "不支持修改账户余额", "not supported to modify account balance time": "不支持修改账户余额时间", + "parent account cannot set last reconciled time": "父账户不能设置最后对账时间", + "cannot set last reconciled time before current value": "不能将最后对账时间设置为早于当前值的时间", "transaction id is invalid": "交易ID无效", "transaction not found": "交易不存在", "transaction type is invalid": "交易类型无效", @@ -1548,6 +1551,7 @@ "Recent 5 years": "最近5年", "Previous Billing Cycle": "上个账单周期", "Current Billing Cycle": "当前账单周期", + "Since Last Reconciled Time": "自最后对账时间起", "Last day": "倒数第1日", "last day": "倒数第1日", "Custom Date": "自定义日期", @@ -1941,6 +1945,9 @@ "You have saved this account": "您已经保存该账户", "Unable to add account": "无法添加账户", "Unable to save account": "无法保存账户", + "Mark as Reconciled": "标记为已对账", + "Last reconciled time have been updated": "最后对账时间已更新", + "Unable to update last reconciled time": "无法更新最后对账时间", "Show Hidden Accounts": "显示隐藏的账户", "Hide Hidden Accounts": "不显示隐藏的账户", "Set Accounts Included in Total": "设置计入总金额的账户", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index 1089eb11..541f6119 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -1112,6 +1112,7 @@ "cannot login by password": "您不能使用密碼登入", "user name is invalid": "使用者名稱無效", "nick name is invalid": "使用者暱稱無效", + "last reconciled time is not enabled": "最後對帳時間未啟用", "unauthorized access": "未授權的登入", "current token is invalid": "目前認證令牌無效", "current token is expired": "目前認證令牌已過期", @@ -1155,6 +1156,8 @@ "not supported to modify account currency": "不支援修改帳戶貨幣", "not supported to modify account balance": "不支援修改帳戶餘額", "not supported to modify account balance time": "不支援修改帳戶餘額時間", + "parent account cannot set last reconciled time": "父帳戶不能設定最後對帳時間", + "cannot set last reconciled time before current value": "不能將最後對帳時間設定為早於目前值的時間", "transaction id is invalid": "交易ID無效", "transaction not found": "交易不存在", "transaction type is invalid": "交易類型無效", @@ -1548,6 +1551,7 @@ "Recent 5 years": "最近5年", "Previous Billing Cycle": "上個帳單週期", "Current Billing Cycle": "當前帳單週期", + "Since Last Reconciled Time": "自最後對帳時間起", "Last day": "倒數第1日", "last day": "倒數第1日", "Custom Date": "自訂日期", @@ -1941,6 +1945,9 @@ "You have saved this account": "您已經儲存此帳戶", "Unable to add account": "無法新增帳戶", "Unable to save account": "無法儲存帳戶", + "Mark as Reconciled": "標記為已對帳", + "Last reconciled time have been updated": "最後對帳時間已更新", + "Unable to update last reconciled time": "無法更新最後對帳時間", "Show Hidden Accounts": "顯示隱藏的帳戶", "Hide Hidden Accounts": "不顯示隱藏的帳戶", "Set Accounts Included in Total": "設定計入總金額的帳戶", diff --git a/src/models/account.ts b/src/models/account.ts index 207664af..6236e3a5 100644 --- a/src/models/account.ts +++ b/src/models/account.ts @@ -614,6 +614,11 @@ export interface AccountModifyRequest { readonly clientSessionId?: string; } +export interface AccountUpdateLastReconciledTimeRequest { + readonly id: string; + readonly lastReconciledTime: number; +} + export interface AccountInfoResponse { readonly id: string; readonly name: string; diff --git a/src/stores/account.ts b/src/stores/account.ts index b63dd780..61be7c10 100644 --- a/src/stores/account.ts +++ b/src/stores/account.ts @@ -903,6 +903,42 @@ export const useAccountsStore = defineStore('accounts', () => { }); } + function updateAccountLastReconciledTime(accountId: string, lastReconciledTime: number): Promise { + return new Promise((resolve, reject) => { + const account = allAccountsMap.value[accountId]; + + services.updateAccountLastReconciledTime({ + id: accountId, + lastReconciledTime: lastReconciledTime + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to last reconciled time' }); + return; + } + + if (account) { + account.lastReconciledTime = lastReconciledTime; + } else { + updateAccountListInvalidState(true); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to update last reconciled time', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + reject({ message: 'Unable to update last reconciled time' }); + } else { + reject(error); + } + }); + }); + } + function changeAccountDisplayOrder({ accountId, from, to, updateListOrder, updateGlobalListOrder }: { accountId: string, from: number, to: number, updateListOrder: boolean, updateGlobalListOrder: boolean }): Promise { const account = allAccountsMap.value[accountId]; @@ -1108,6 +1144,7 @@ export const useAccountsStore = defineStore('accounts', () => { loadAllAccounts, getAccount, saveAccount, + updateAccountLastReconciledTime, changeAccountDisplayOrder, updateAccountDisplayOrders, hideAccount, diff --git a/src/stores/transaction.ts b/src/stores/transaction.ts index 6a12a4ba..76b77ad5 100644 --- a/src/stores/transaction.ts +++ b/src/stores/transaction.ts @@ -746,6 +746,9 @@ export const useTransactionsStore = defineStore('transactions', () => { if (DateRange.isBillingCycle(transactionsFilter.value.dateType) && (!accountsStore.getAccountStatementDate(filter.accountIds) || accountsStore.getAccountStatementDate(filter.accountIds) !== accountsStore.getAccountStatementDate(transactionsFilter.value.accountIds))) { transactionsFilter.value.dateType = DateRange.Custom.type; + } else if (DateRange.isLastReconciledTimeRange(transactionsFilter.value.dateType) && + (!accountsStore.allAccountsMap[filter.accountIds] || accountsStore.allAccountsMap[filter.accountIds]?.lastReconciledTime !== accountsStore.allAccountsMap[transactionsFilter.value.accountIds]?.lastReconciledTime)) { + transactionsFilter.value.dateType = DateRange.Custom.type; } transactionsFilter.value.accountIds = filter.accountIds; @@ -793,7 +796,9 @@ export const useTransactionsStore = defineStore('transactions', () => { querys.push('dateType=' + transactionsFilter.value.dateType); - if (DateRange.isBillingCycle(transactionsFilter.value.dateType) || transactionsFilter.value.dateType === DateRange.Custom.type) { + if (DateRange.isBillingCycle(transactionsFilter.value.dateType) + || DateRange.isLastReconciledTimeRange(transactionsFilter.value.dateType) + || transactionsFilter.value.dateType === DateRange.Custom.type) { querys.push('maxTime=' + transactionsFilter.value.maxTime); querys.push('minTime=' + transactionsFilter.value.minTime); } diff --git a/src/views/base/accounts/AccountListPageBase.ts b/src/views/base/accounts/AccountListPageBase.ts index fadcf140..4fe0ce10 100644 --- a/src/views/base/accounts/AccountListPageBase.ts +++ b/src/views/base/accounts/AccountListPageBase.ts @@ -35,6 +35,7 @@ export function useAccountListPageBase() { const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); const fiscalYearStart = computed(() => userStore.currentUserFiscalYearStart); const defaultCurrency = computed(() => userStore.currentUserDefaultCurrency); + const useLastReconciledTime = computed(() => userStore.currentUserUseLastReconciledTime); const allAccounts = computed(() => accountsStore.allAccounts); const allCategorizedAccountsMap = computed>(() => accountsStore.allCategorizedAccountsMap); @@ -99,6 +100,7 @@ export function useAccountListPageBase() { firstDayOfWeek, fiscalYearStart, defaultCurrency, + useLastReconciledTime, allAccounts, allCategorizedAccountsMap, allAccountCount, diff --git a/src/views/base/accounts/ReconciliationStatementPageBase.ts b/src/views/base/accounts/ReconciliationStatementPageBase.ts index 66d2ee4e..5446f514 100644 --- a/src/views/base/accounts/ReconciliationStatementPageBase.ts +++ b/src/views/base/accounts/ReconciliationStatementPageBase.ts @@ -27,6 +27,7 @@ import { replaceAll } from '@/lib/common.ts'; import { getUtcOffsetByUtcOffsetMinutes, getTimezoneOffsetMinutes, + getCurrentUnixTime, parseDateTimeFromUnixTime, parseDateTimeFromUnixTimeWithTimezoneOffset } from '@/lib/datetime.ts'; @@ -53,6 +54,7 @@ export function useReconciliationStatementPageBase() { const accountId = ref(''); const startTime = ref(0); const endTime = ref(0); + const pageOpenTime = ref(getCurrentUnixTime()); const reconciliationStatements = ref(undefined); const chartDataDateAggregationType = ref(ChartDateAggregationType.Day.type); const timezoneUsedForDateRange = ref(TimezoneTypeForStatistics.ApplicationTimezone.type); @@ -61,6 +63,7 @@ export function useReconciliationStatementPageBase() { const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); const fiscalYearStart = computed(() => userStore.currentUserFiscalYearStart); const defaultCurrency = computed(() => userStore.currentUserDefaultCurrency); + const useLastReconciledTime = computed(() => userStore.currentUserUseLastReconciledTime); const allChartTypes = computed(() => getAllAccountBalanceTrendChartTypes()); const allDateAggregationTypes = computed(() => getAllStatisticsDateAggregationTypesWithShortName(StatisticsAnalysisType.AssetTrends, !!currentAccountStatementDate.value)); @@ -69,8 +72,23 @@ export function useReconciliationStatementPageBase() { const currentAccount = computed(() => allAccountsMap.value[accountId.value]); const currentAccountCurrency = computed(() => currentAccount.value?.currency ?? defaultCurrency.value); const currentAccountStatementDate = computed(() => accountsStore.getAccountStatementDate(accountId.value) || undefined); + const currentAccountLastReconciledTime = computed(() => currentAccount.value?.lastReconciledTime); const isCurrentLiabilityAccount = computed(() => currentAccount.value?.isLiability ?? false); + const newLastReconciledTime = computed(() => { + if (!currentAccount.value || !useLastReconciledTime.value) { + return undefined; + } + + const actualEndTime: number = endTime.value === 0 ? pageOpenTime.value : Math.min(endTime.value, pageOpenTime.value); + + if (!currentAccountLastReconciledTime.value || actualEndTime > currentAccountLastReconciledTime.value) { + return actualEndTime; + } else { + return undefined; + } + }); + const exportFileName = computed(() => { const nickname = userStore.currentUserNickname; @@ -124,6 +142,10 @@ export function useReconciliationStatementPageBase() { } }); + function updatePageOpenTime() { + pageOpenTime.value = getCurrentUnixTime(); + } + function setReconciliationStatements(response: TransactionReconciliationStatementResponse | undefined) { if (!response) { reconciliationStatements.value = undefined; @@ -305,13 +327,16 @@ export function useReconciliationStatementPageBase() { firstDayOfWeek, fiscalYearStart, defaultCurrency, + useLastReconciledTime, allChartTypes, allDateAggregationTypes, allTimezoneTypesUsedForDateRange, currentAccount, currentAccountCurrency, currentAccountStatementDate, + currentAccountLastReconciledTime, isCurrentLiabilityAccount, + newLastReconciledTime, exportFileName, displayStartDateTime, displayEndDateTime, @@ -321,6 +346,7 @@ export function useReconciliationStatementPageBase() { displayOpeningBalance, displayClosingBalance, // functions + updatePageOpenTime, setReconciliationStatements, getDisplayTransactionType, getDisplayDateTime, diff --git a/src/views/base/statistics/StatisticsSettingPageBase.ts b/src/views/base/statistics/StatisticsSettingPageBase.ts index 08f74084..97285c2c 100644 --- a/src/views/base/statistics/StatisticsSettingPageBase.ts +++ b/src/views/base/statistics/StatisticsSettingPageBase.ts @@ -24,10 +24,10 @@ export function useStatisticsSettingPageBase() { const allTimezoneTypesUsedForStatistics = computed(() => getAllTimezoneTypesUsedForStatistics()); const allSortingTypes = computed(() => getAllStatisticsSortingTypes()); const allCategoricalChartTypes = computed(() => getAllCategoricalChartTypes()); - const allCategoricalChartDateRanges = computed(() => getAllDateRanges(DateRangeScene.Normal, false)); + const allCategoricalChartDateRanges = computed(() => getAllDateRanges(DateRangeScene.Normal, {})); const allTrendChartTypes = computed(() => getAllTrendChartTypes()); - const allTrendChartDateRanges = computed(() => getAllDateRanges(DateRangeScene.TrendAnalysis, false)); - const allAssetTrendsChartDateRanges = computed(() => getAllDateRanges(DateRangeScene.AssetTrends, false)); + const allTrendChartDateRanges = computed(() => getAllDateRanges(DateRangeScene.TrendAnalysis, {})); + const allAssetTrendsChartDateRanges = computed(() => getAllDateRanges(DateRangeScene.AssetTrends, {})); const defaultChartDataType = computed({ get: () => settingsStore.appSettings.statistics.defaultChartDataType, diff --git a/src/views/base/statistics/StatisticsTransactionPageBase.ts b/src/views/base/statistics/StatisticsTransactionPageBase.ts index b686a78c..4648ac04 100644 --- a/src/views/base/statistics/StatisticsTransactionPageBase.ts +++ b/src/views/base/statistics/StatisticsTransactionPageBase.ts @@ -63,11 +63,11 @@ export function useStatisticsTransactionPageBase() { const allDateRanges = computed(() => { if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) { - return getAllDateRanges(DateRangeScene.Normal, true); + return getAllDateRanges(DateRangeScene.Normal, { includeCustom: true }); } else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) { - return getAllDateRanges(DateRangeScene.TrendAnalysis, true); + return getAllDateRanges(DateRangeScene.TrendAnalysis, { includeCustom: true }); } else if (analysisType.value === StatisticsAnalysisType.AssetTrends) { - return getAllDateRanges(DateRangeScene.AssetTrends, true); + return getAllDateRanges(DateRangeScene.AssetTrends, { includeCustom: true }); } else { return []; } diff --git a/src/views/base/transactions/TransactionListPageBase.ts b/src/views/base/transactions/TransactionListPageBase.ts index 2183de0e..c196e4e8 100644 --- a/src/views/base/transactions/TransactionListPageBase.ts +++ b/src/views/base/transactions/TransactionListPageBase.ts @@ -105,7 +105,11 @@ export function useTransactionListPageBase() { const showTotalAmountInTransactionListPage = computed(() => settingsStore.appSettings.showTotalAmountInTransactionListPage); const showTagInTransactionListPage = computed(() => settingsStore.appSettings.showTagInTransactionListPage); - const allDateRanges = computed(() => getAllDateRanges(DateRangeScene.Normal, true, !!accountsStore.getAccountStatementDate(query.value.accountIds))); + const allDateRanges = computed(() => getAllDateRanges(DateRangeScene.Normal, { + includeCustom: true, + includeBillingCycle: !!accountsStore.getAccountStatementDate(query.value.accountIds), + includeLastReconciledTimeRange: !!allAccountsMap.value[query.value.accountIds]?.lastReconciledTime + })); const allAccounts = computed(() => accountsStore.allMixedPlainAccounts); const allAccountsMap = computed>(() => accountsStore.allAccountsMap); diff --git a/src/views/desktop/accounts/ListPage.vue b/src/views/desktop/accounts/ListPage.vue index 0543f08c..77d56abc 100644 --- a/src/views/desktop/accounts/ListPage.vue +++ b/src/views/desktop/accounts/ListPage.vue @@ -256,6 +256,12 @@ {{ tt('More') }} + + { + loading.value = false; + snackbar.value?.showMessage('Last reconciled time have been updated'); + + if (accountsStore.accountListStateInvalid && !loading.value) { + reload(false); + } + + }).catch(error => { + loading.value = false; + + if (error) { + snackbar.value?.showError(error); + } + }); +} + function moveAllTransactions(account: Account): void { moveAllTransactionsDialog.value?.open(account).then(() => { snackbar.value?.showMessage('All transactions in this account have been moved.'); diff --git a/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue b/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue index 16c3d4bc..ba94c09e 100644 --- a/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue +++ b/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue @@ -1,12 +1,13 @@