add reconciliation statement in desktop version
This commit is contained in:
@@ -344,6 +344,7 @@ func startWebServer(c *core.CliContext) error {
|
||||
apiV1Route.GET("/transactions/count.json", bindApi(api.Transactions.TransactionCountHandler))
|
||||
apiV1Route.GET("/transactions/list.json", bindApi(api.Transactions.TransactionListHandler))
|
||||
apiV1Route.GET("/transactions/list/by_month.json", bindApi(api.Transactions.TransactionMonthListHandler))
|
||||
apiV1Route.GET("/transactions/reconciliation_statements.json", bindApi(api.Transactions.TransactionReconciliationStatementHandler))
|
||||
apiV1Route.GET("/transactions/statistics.json", bindApi(api.Transactions.TransactionStatisticsHandler))
|
||||
apiV1Route.GET("/transactions/statistics/trends.json", bindApi(api.Transactions.TransactionStatisticsTrendsHandler))
|
||||
apiV1Route.GET("/transactions/amounts.json", bindApi(api.Transactions.TransactionAmountsHandler))
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||
)
|
||||
|
||||
const pageCountForAccountStatement = 1000
|
||||
|
||||
// TransactionsApi represents transaction api
|
||||
type TransactionsApi struct {
|
||||
ApiUsingConfig
|
||||
@@ -286,6 +288,107 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.WebContext) (any,
|
||||
return transactionResps, nil
|
||||
}
|
||||
|
||||
// TransactionReconciliationStatementHandler returns transaction reconciliation statement list of current user
|
||||
func (a *TransactionsApi) TransactionReconciliationStatementHandler(c *core.WebContext) (any, *errs.Error) {
|
||||
var reconciliationStatementRequest models.TransactionReconciliationStatementRequest
|
||||
err := c.ShouldBindQuery(&reconciliationStatementRequest)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[transactions.TransactionReconciliationStatementHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
utcOffset, err := c.GetClientTimezoneOffset()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[transactions.TransactionReconciliationStatementHandler] cannot get client timezone offset, because %s", err.Error())
|
||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
user, err := a.users.GetUserById(c, uid)
|
||||
|
||||
if err != nil {
|
||||
if !errs.IsCustomError(err) {
|
||||
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to get user, because %s", err.Error())
|
||||
}
|
||||
|
||||
return nil, errs.ErrUserNotFound
|
||||
}
|
||||
|
||||
account, err := a.accounts.GetAccountByAccountId(c, uid, reconciliationStatementRequest.AccountId)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to get account \"id:%d\" for user \"uid:%d\", because %s", reconciliationStatementRequest.AccountId, uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
if account.Type != models.ACCOUNT_TYPE_SINGLE_ACCOUNT {
|
||||
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] account \"id:%d\" for user \"uid:%d\" is not a single account", reconciliationStatementRequest.AccountId, uid)
|
||||
return nil, errs.ErrAccountTypeInvalid
|
||||
}
|
||||
|
||||
maxTransactionTime := int64(0)
|
||||
|
||||
if reconciliationStatementRequest.EndTime > 0 {
|
||||
maxTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(reconciliationStatementRequest.EndTime)
|
||||
}
|
||||
|
||||
minTransactionTime := int64(0)
|
||||
|
||||
if reconciliationStatementRequest.StartTime > 0 {
|
||||
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(reconciliationStatementRequest.StartTime)
|
||||
}
|
||||
|
||||
transactionsWithAccountBalance, err := a.transactions.GetAllTransactionsWithAccountBalanceByMaxTime(c, uid, pageCountForAccountStatement, maxTransactionTime, minTransactionTime, reconciliationStatementRequest.AccountId)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to get transactions from \"%d\" to \"%d\" for user \"uid:%d\", because %s", reconciliationStatementRequest.StartTime, reconciliationStatementRequest.EndTime, uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
transactions := make([]*models.Transaction, len(transactionsWithAccountBalance))
|
||||
transactionAccountBalanceMap := make(map[int64]*models.TransactionWithAccountBalance, len(transactionsWithAccountBalance))
|
||||
|
||||
for i := 0; i < len(transactionsWithAccountBalance); i++ {
|
||||
transactionWithBalance := transactionsWithAccountBalance[i]
|
||||
transactions[i] = transactionWithBalance.Transaction
|
||||
transactionAccountBalanceMap[transactionWithBalance.TransactionId] = transactionWithBalance
|
||||
transactionAccountBalanceMap[transactionWithBalance.RelatedId] = transactionWithBalance
|
||||
}
|
||||
|
||||
transactionResult, err := a.getTransactionResponseListResult(c, user, transactions, utcOffset, false, true, true, true)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
responseItems := make([]*models.TransactionReconciliationStatementResponseItem, len(transactionResult))
|
||||
|
||||
for i := 0; i < len(transactionResult); i++ {
|
||||
transactionResult := transactionResult[i]
|
||||
accountBalance := int64(0)
|
||||
|
||||
if transactionWithBalance, exists := transactionAccountBalanceMap[transactionResult.Id]; exists {
|
||||
accountBalance = transactionWithBalance.AccountBalance
|
||||
} else {
|
||||
log.Warnf(c, "[transactions.TransactionReconciliationStatementHandler] missing account balance for transaction \"id:%d\" of user \"uid:%d\"", transactionResult.Id, uid)
|
||||
}
|
||||
|
||||
responseItems[i] = &models.TransactionReconciliationStatementResponseItem{
|
||||
TransactionInfoResponse: transactionResult,
|
||||
AccountBalance: accountBalance,
|
||||
}
|
||||
}
|
||||
|
||||
reconciliationStatementResp := &models.TransactionReconciliationStatementResponse{
|
||||
Transactions: responseItems,
|
||||
}
|
||||
|
||||
return reconciliationStatementResp, nil
|
||||
}
|
||||
|
||||
// TransactionStatisticsHandler returns transaction statistics of current user
|
||||
func (a *TransactionsApi) TransactionStatisticsHandler(c *core.WebContext) (any, *errs.Error) {
|
||||
var statisticReq models.TransactionStatisticRequest
|
||||
|
||||
@@ -120,6 +120,12 @@ type Transaction struct {
|
||||
DeletedUnixTime int64
|
||||
}
|
||||
|
||||
// TransactionWithAccountBalance represents a transaction item with account balance
|
||||
type TransactionWithAccountBalance struct {
|
||||
*Transaction
|
||||
AccountBalance int64
|
||||
}
|
||||
|
||||
// TransactionGeoLocationRequest represents all parameters of transaction geographic location info update request
|
||||
type TransactionGeoLocationRequest struct {
|
||||
Latitude float64 `json:"latitude" binding:"required"`
|
||||
@@ -222,6 +228,13 @@ type TransactionListInMonthByPageRequest struct {
|
||||
TrimTag bool `form:"trim_tag"`
|
||||
}
|
||||
|
||||
// TransactionReconciliationStatementRequest represents all parameters of transaction reconciliation statement request
|
||||
type TransactionReconciliationStatementRequest struct {
|
||||
AccountId int64 `form:"account_id,string" binding:"required,min=1"`
|
||||
StartTime int64 `form:"start_time"`
|
||||
EndTime int64 `form:"end_time"`
|
||||
}
|
||||
|
||||
// TransactionStatisticRequest represents all parameters of transaction statistic request
|
||||
type TransactionStatisticRequest struct {
|
||||
StartTime int64 `form:"start_time" binding:"min=0"`
|
||||
@@ -322,6 +335,17 @@ type TransactionInfoPageWrapperResponse2 struct {
|
||||
TotalCount int64 `json:"totalCount"`
|
||||
}
|
||||
|
||||
// TransactionReconciliationStatementResponseItem represents a transaction reconciliation statement response
|
||||
type TransactionReconciliationStatementResponseItem struct {
|
||||
*TransactionInfoResponse
|
||||
AccountBalance int64 `json:"accountBalance"`
|
||||
}
|
||||
|
||||
// TransactionReconciliationStatementResponse represents the response of all transaction reconciliation statement response
|
||||
type TransactionReconciliationStatementResponse struct {
|
||||
Transactions []*TransactionReconciliationStatementResponseItem `json:"transactions"`
|
||||
}
|
||||
|
||||
// TransactionStatisticResponse represents transaction statistic response
|
||||
type TransactionStatisticResponse struct {
|
||||
StartTime int64 `json:"startTime"`
|
||||
|
||||
@@ -56,6 +56,28 @@ func (s *AccountService) GetAllAccountsByUid(c core.Context, uid int64) ([]*mode
|
||||
return accounts, err
|
||||
}
|
||||
|
||||
// GetAccountByAccountId returns account model according to account id
|
||||
func (s *AccountService) GetAccountByAccountId(c core.Context, uid int64, accountId int64) (*models.Account, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
if accountId <= 0 {
|
||||
return nil, errs.ErrAccountIdInvalid
|
||||
}
|
||||
|
||||
account := &models.Account{}
|
||||
has, err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=? AND account_id=?", uid, false, accountId).Get(account)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, errs.ErrAccountNotFound
|
||||
}
|
||||
|
||||
return account, err
|
||||
}
|
||||
|
||||
// GetAccountAndSubAccountsByAccountId returns account model and sub-account models according to account id
|
||||
func (s *AccountService) GetAccountAndSubAccountsByAccountId(c core.Context, uid int64, accountId int64) ([]*models.Account, error) {
|
||||
if uid <= 0 {
|
||||
|
||||
@@ -107,6 +107,72 @@ func (s *TransactionService) GetAllSpecifiedTransactions(c core.Context, uid int
|
||||
return allTransactions, nil
|
||||
}
|
||||
|
||||
// GetAllTransactionsWithAccountBalanceByMaxTime returns account statement within time range
|
||||
func (s *TransactionService) GetAllTransactionsWithAccountBalanceByMaxTime(c core.Context, uid int64, pageCount int32, maxTransactionTime int64, minTransactionTime int64, accountId int64) ([]*models.TransactionWithAccountBalance, error) {
|
||||
if maxTransactionTime <= 0 {
|
||||
maxTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(time.Now().Unix())
|
||||
}
|
||||
|
||||
var allTransactions []*models.Transaction
|
||||
|
||||
for maxTransactionTime > 0 {
|
||||
transactions, err := s.GetTransactionsByMaxTime(c, uid, maxTransactionTime, 0, 0, nil, []int64{accountId}, nil, false, models.TRANSACTION_TAG_FILTER_HAS_ANY, "", "", 1, pageCount, false, true)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allTransactions = append(allTransactions, transactions...)
|
||||
|
||||
if len(transactions) < int(pageCount) {
|
||||
maxTransactionTime = 0
|
||||
break
|
||||
}
|
||||
|
||||
maxTransactionTime = transactions[len(transactions)-1].TransactionTime - 1
|
||||
}
|
||||
|
||||
allTransactionsAndAccountBalance := make([]*models.TransactionWithAccountBalance, 0, len(allTransactions))
|
||||
|
||||
if len(allTransactions) < 1 {
|
||||
return allTransactionsAndAccountBalance, nil
|
||||
}
|
||||
|
||||
accumulatedBalance := int64(0)
|
||||
|
||||
for i := len(allTransactions) - 1; i >= 0; i-- {
|
||||
transaction := allTransactions[i]
|
||||
|
||||
if transaction.Type == models.TRANSACTION_DB_TYPE_MODIFY_BALANCE {
|
||||
accumulatedBalance = accumulatedBalance + transaction.RelatedAccountAmount
|
||||
} else if transaction.Type == models.TRANSACTION_DB_TYPE_INCOME {
|
||||
accumulatedBalance = accumulatedBalance + transaction.Amount
|
||||
} else if transaction.Type == models.TRANSACTION_DB_TYPE_EXPENSE {
|
||||
accumulatedBalance = accumulatedBalance - transaction.Amount
|
||||
} else if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT {
|
||||
accumulatedBalance = accumulatedBalance - transaction.Amount
|
||||
} else if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||
accumulatedBalance = accumulatedBalance + transaction.Amount
|
||||
} else {
|
||||
log.Errorf(c, "[transactions.GetAllTransactionsWithAccountBalanceByMaxTime] trasaction type (%d) is invalid (id:%d)", transaction.TransactionId, transaction.Type)
|
||||
return nil, errs.ErrTransactionTypeInvalid
|
||||
}
|
||||
|
||||
if transaction.TransactionTime < minTransactionTime {
|
||||
continue
|
||||
}
|
||||
|
||||
transactionsAndAccountBalance := &models.TransactionWithAccountBalance{
|
||||
Transaction: transaction,
|
||||
AccountBalance: accumulatedBalance,
|
||||
}
|
||||
|
||||
allTransactionsAndAccountBalance = append(allTransactionsAndAccountBalance, transactionsAndAccountBalance)
|
||||
}
|
||||
|
||||
return allTransactionsAndAccountBalance, nil
|
||||
}
|
||||
|
||||
// GetTransactionsByMaxTime returns transactions before given time
|
||||
func (s *TransactionService) GetTransactionsByMaxTime(c core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, amountFilter string, keyword string, page int32, count int32, needOneMoreItem bool, noDuplicated bool) ([]*models.Transaction, error) {
|
||||
if uid <= 0 {
|
||||
|
||||
@@ -326,7 +326,7 @@ function getTimerPickerItemStyle(textualValue: string, textualCurrentValue: stri
|
||||
}
|
||||
}
|
||||
|
||||
let angle = -24 * valueDiff;
|
||||
const angle = -24 * valueDiff;
|
||||
|
||||
if (angle > 180) {
|
||||
return '';
|
||||
|
||||
+2
-2
@@ -923,8 +923,8 @@ export function getFullMonthDateRange(minTime: number, maxTime: number, firstDay
|
||||
export function getCombinedDateAndTimeValues(date: Date, hour: string, minute: string, second: string, meridiemIndicator: string, is24Hour: boolean): Date {
|
||||
const newDateTime = new Date(date.valueOf());
|
||||
let hours = parseInt(hour);
|
||||
let minutes = parseInt(minute);
|
||||
let seconds = parseInt(second);
|
||||
const minutes = parseInt(minute);
|
||||
const seconds = parseInt(second);
|
||||
|
||||
if (!is24Hour) {
|
||||
if (hours === 12) {
|
||||
|
||||
@@ -65,6 +65,8 @@ import type {
|
||||
TransactionInfoResponse,
|
||||
TransactionInfoPageWrapperResponse,
|
||||
TransactionInfoPageWrapperResponse2,
|
||||
TransactionReconciliationStatementRequest,
|
||||
TransactionReconciliationStatementResponse,
|
||||
TransactionStatisticRequest,
|
||||
TransactionStatisticResponse,
|
||||
TransactionStatisticTrendsRequest,
|
||||
@@ -413,6 +415,9 @@ export default {
|
||||
const keyword = encodeURIComponent(req.keyword);
|
||||
return axios.get<ApiResponse<TransactionInfoPageWrapperResponse2>>(`v1/transactions/list/by_month.json?year=${req.year}&month=${req.month}&type=${req.type}&category_ids=${req.categoryIds}&account_ids=${req.accountIds}&tag_ids=${req.tagIds}&tag_filter_type=${req.tagFilterType}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`);
|
||||
},
|
||||
getReconciliationStatements: (req: TransactionReconciliationStatementRequest): ApiResponsePromise<TransactionReconciliationStatementResponse> => {
|
||||
return axios.get<ApiResponse<TransactionReconciliationStatementResponse>>(`v1/transactions/reconciliation_statements.json?account_id=${req.accountId}&start_time=${req.startTime}&end_time=${req.endTime}`);
|
||||
},
|
||||
getTransactionStatistics: (req: TransactionStatisticRequest): ApiResponsePromise<TransactionStatisticResponse> => {
|
||||
const queryParams = [];
|
||||
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Ausgabe",
|
||||
"Income": "Einkommen",
|
||||
"Transfer": "Überweisung",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Bargeld",
|
||||
"Checking Account": "Girokonto",
|
||||
"Credit Card": "Kreditkarte",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
|
||||
"Unable to delete this account": "Konto kann nicht gelöscht werden",
|
||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Transaktion",
|
||||
"Transactions": "Transaktionen",
|
||||
"Transaction Pictures": "Transaktionsbilder",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "Transaktionsbeschreibung suchen",
|
||||
"Unable to retrieve transaction list": "Transaktionsliste kann nicht abgerufen werden",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Benutzerdefinierter Datumsbereich",
|
||||
"Transaction Detail": "Transaktionsdetails",
|
||||
"No transaction data": "Keine Transaktionsdaten",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Gesamtverbindlichkeiten",
|
||||
"Total Expense": "Gesamtausgaben",
|
||||
"Total Income": "Gesamteinnahmen",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Gesamtsaldo",
|
||||
"Expense By Account": "Ausgaben nach Konto",
|
||||
"Expense By Primary Category": "Ausgaben nach Primärkategorie",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Expense",
|
||||
"Income": "Income",
|
||||
"Transfer": "Transfer",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Cash",
|
||||
"Checking Account": "Checking Account",
|
||||
"Credit Card": "Credit Card",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
|
||||
"Unable to delete this account": "Unable to delete this account",
|
||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Transaction",
|
||||
"Transactions": "Transactions",
|
||||
"Transaction Pictures": "Transaction Pictures",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "Search transaction description",
|
||||
"Unable to retrieve transaction list": "Unable to retrieve transaction list",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Custom Date Range",
|
||||
"Transaction Detail": "Transaction Detail",
|
||||
"No transaction data": "No transaction data",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Total Liabilities",
|
||||
"Total Expense": "Total Expense",
|
||||
"Total Income": "Total Income",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Total Balance",
|
||||
"Expense By Account": "Expense By Account",
|
||||
"Expense By Primary Category": "Expense By Primary Category",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Gastos",
|
||||
"Income": "Ingresos",
|
||||
"Transfer": "Transferencias",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Dinero",
|
||||
"Checking Account": "Cuenta de cheques",
|
||||
"Credit Card": "Tarjeta de crédito",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
|
||||
"Unable to delete this account": "No se puede eliminar esta cuenta",
|
||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Transacción",
|
||||
"Transactions": "Transacciones",
|
||||
"Transaction Pictures": "Imágenes de transacciones",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "Buscar descripción de transacción",
|
||||
"Unable to retrieve transaction list": "No se puede recuperar la lista de transacciones",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Rango de fechas personalizado",
|
||||
"Transaction Detail": "Detalle de la transacción",
|
||||
"No transaction data": "Sin datos de transacción",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Pasivos totales",
|
||||
"Total Expense": "Gasto total",
|
||||
"Total Income": "Ingresos totales",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Saldo total",
|
||||
"Expense By Account": "Gasto por cuenta",
|
||||
"Expense By Primary Category": "Gasto por categoría primaria",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Spesa",
|
||||
"Income": "Entrata",
|
||||
"Transfer": "Trasferimento",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Contanti",
|
||||
"Checking Account": "Conto corrente",
|
||||
"Credit Card": "Carta di credito",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Sei sicuro di voler eliminare questo sotto-account?",
|
||||
"Unable to delete this account": "Impossibile eliminare questo account",
|
||||
"Unable to delete this sub-account": "Impossibile eliminare questo sotto-account",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Transazione",
|
||||
"Transactions": "Transazioni",
|
||||
"Transaction Pictures": "Immagini transazione",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "Cerca descrizione transazione",
|
||||
"Unable to retrieve transaction list": "Impossibile recuperare l'elenco delle transazioni",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Intervallo date personalizzato",
|
||||
"Transaction Detail": "Dettaglio transazione",
|
||||
"No transaction data": "Nessun dato di transazione",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Passività totali",
|
||||
"Total Expense": "Spesa totale",
|
||||
"Total Income": "Entrata totale",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Saldo totale",
|
||||
"Expense By Account": "Spesa per conto",
|
||||
"Expense By Primary Category": "Spesa per categoria principale",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "支出",
|
||||
"Income": "収入",
|
||||
"Transfer": "振替",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "現金",
|
||||
"Checking Account": "当座預金口座",
|
||||
"Credit Card": "クレジットカード",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
|
||||
"Unable to delete this account": "この口座を削除できません",
|
||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "取引",
|
||||
"Transactions": "取引",
|
||||
"Transaction Pictures": "取引の写真",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "取引の説明を検索",
|
||||
"Unable to retrieve transaction list": "取引リストを取得できません",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "カスタム日付範囲",
|
||||
"Transaction Detail": "取引の詳細",
|
||||
"No transaction data": "取引データがありません",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "総負債",
|
||||
"Total Expense": "総支出",
|
||||
"Total Income": "総収入",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "残高合計",
|
||||
"Expense By Account": "口座別の支出",
|
||||
"Expense By Primary Category": "一次カテゴリ別の支出",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Despesa",
|
||||
"Income": "Renda",
|
||||
"Transfer": "Transferência",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Dinheiro",
|
||||
"Checking Account": "Conta Corrente",
|
||||
"Credit Card": "Cartão de Crédito",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Tem certeza de que deseja deletar esta subconta?",
|
||||
"Unable to delete this account": "Não foi possível deletar esta conta",
|
||||
"Unable to delete this sub-account": "Não foi possível deletar esta subconta",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Transação",
|
||||
"Transactions": "Transações",
|
||||
"Transaction Pictures": "Imagens das Transações",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Falha ao carregar a imagem, por favor verifique se as configurações \"domain\" e \"root_url\" estão configuradas corretamente.",
|
||||
"Search transaction description": "Pesquisar descrição da transação",
|
||||
"Unable to retrieve transaction list": "Incapaz de recuperar a lista de transações",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Intervalo de Datas Personalizado",
|
||||
"Transaction Detail": "Detalhes da Transação",
|
||||
"No transaction data": "Sem dados de transação",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Total de Passivos",
|
||||
"Total Expense": "Despesa Total",
|
||||
"Total Income": "Renda Total",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Saldo Total",
|
||||
"Expense By Account": "Despesa por Conta",
|
||||
"Expense By Primary Category": "Despesa por Categoria Primária",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Расход",
|
||||
"Income": "Доход",
|
||||
"Transfer": "Перевод",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Наличные",
|
||||
"Checking Account": "Текущий счет",
|
||||
"Credit Card": "Кредитная карта",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
|
||||
"Unable to delete this account": "Не удалось удалить этот счет",
|
||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Транзакция",
|
||||
"Transactions": "Транзакции",
|
||||
"Transaction Pictures": "Изображения транзакций",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "Поиск описания транзакции",
|
||||
"Unable to retrieve transaction list": "Не удалось получить список транзакций",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Пользовательский диапазон дат",
|
||||
"Transaction Detail": "Детали транзакции",
|
||||
"No transaction data": "Нет данных о транзакциях",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Общие обязательства",
|
||||
"Total Expense": "Общий расход",
|
||||
"Total Income": "Общий доход",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Общий баланс",
|
||||
"Expense By Account": "Расходы по счетам",
|
||||
"Expense By Primary Category": "Расходы по основной категории",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Витрати",
|
||||
"Income": "Доходи",
|
||||
"Transfer": "Перекази",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Готівка",
|
||||
"Checking Account": "Розрахунковий рахунок",
|
||||
"Credit Card": "Кредитна картка",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Ви впевнені, що хочете видалити цей субрахунок?",
|
||||
"Unable to delete this account": "Не вдалося видалити цей рахунок",
|
||||
"Unable to delete this sub-account": "Не вдалося видалити цей субрахунок",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Транзакція",
|
||||
"Transactions": "Транзакції",
|
||||
"Transaction Pictures": "Зображення транзакцій",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "Пошук за описом транзакції",
|
||||
"Unable to retrieve transaction list": "Не вдалося отримати список транзакцій",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Користувацький діапазон дат",
|
||||
"Transaction Detail": "Деталі транзакції",
|
||||
"No transaction data": "Немає даних про транзакції",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Загальні зобов’язання",
|
||||
"Total Expense": "Загальні витрати",
|
||||
"Total Income": "Загальний дохід",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Загальний баланс",
|
||||
"Expense By Account": "Витрати за рахунками",
|
||||
"Expense By Primary Category": "Витрати за основними категоріями",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "Chi phí",
|
||||
"Income": "Thu nhập",
|
||||
"Transfer": "Chuyển khoản",
|
||||
"Transfer In": "Transfer In",
|
||||
"Transfer Out": "Transfer Out",
|
||||
"Cash": "Tiền mặt",
|
||||
"Checking Account": "Tài khoản séc",
|
||||
"Credit Card": "Thẻ tín dụng",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "Are you sure you want to delete this sub-account?",
|
||||
"Unable to delete this account": "Không thể xóa tài khoản này",
|
||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||
"Reconciliation Statement": "Reconciliation Statement",
|
||||
"Transaction": "Giao dịch",
|
||||
"Transactions": "Giao dịch",
|
||||
"Transaction Pictures": "Ảnh giao dịch",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.",
|
||||
"Search transaction description": "Tìm kiếm mô tả giao dịch",
|
||||
"Unable to retrieve transaction list": "Không thể lấy danh sách giao dịch",
|
||||
"Unable to retrieve reconciliation statements": "Unable to retrieve reconciliation statements",
|
||||
"Custom Date Range": "Phạm vi ngày tùy chỉnh",
|
||||
"Transaction Detail": "Chi tiết giao dịch",
|
||||
"No transaction data": "Không có dữ liệu giao dịch",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "Tổng nợ phải trả",
|
||||
"Total Expense": "Tổng chi phí",
|
||||
"Total Income": "Tổng thu nhập",
|
||||
"Total Outflows": "Total Outflows",
|
||||
"Total Inflows": "Total Inflows",
|
||||
"Total Balance": "Tổng số dư",
|
||||
"Expense By Account": "Chi phí theo tài khoản",
|
||||
"Expense By Primary Category": "Chi phí theo danh mục chính",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "支出",
|
||||
"Income": "收入",
|
||||
"Transfer": "转账",
|
||||
"Transfer In": "转入",
|
||||
"Transfer Out": "转出",
|
||||
"Cash": "现金",
|
||||
"Checking Account": "借记账户",
|
||||
"Credit Card": "信用卡",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "您确定要删除该子账户?",
|
||||
"Unable to delete this account": "无法删除该账户",
|
||||
"Unable to delete this sub-account": "无法删除该子账户",
|
||||
"Reconciliation Statement": "对账单",
|
||||
"Transaction": "交易",
|
||||
"Transactions": "交易",
|
||||
"Transaction Pictures": "交易图片",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "无法加载图片,请检查配置 \"domain\" 和 \"root_url\" 是否设置正确。",
|
||||
"Search transaction description": "搜索交易描述",
|
||||
"Unable to retrieve transaction list": "无法获取交易列表",
|
||||
"Unable to retrieve reconciliation statements": "无法获取对账单",
|
||||
"Custom Date Range": "自定义日期范围",
|
||||
"Transaction Detail": "交易详情",
|
||||
"No transaction data": "没有交易数据",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "总负债",
|
||||
"Total Expense": "总支出",
|
||||
"Total Income": "总收入",
|
||||
"Total Outflows": "总流出",
|
||||
"Total Inflows": "总流入",
|
||||
"Total Balance": "总结余",
|
||||
"Expense By Account": "账户支出",
|
||||
"Expense By Primary Category": "一级分类支出",
|
||||
|
||||
@@ -1562,6 +1562,8 @@
|
||||
"Expense": "支出",
|
||||
"Income": "收入",
|
||||
"Transfer": "轉帳",
|
||||
"Transfer In": "轉入",
|
||||
"Transfer Out": "轉出",
|
||||
"Cash": "現金",
|
||||
"Checking Account": "支票帳戶",
|
||||
"Credit Card": "信用卡",
|
||||
@@ -1627,6 +1629,7 @@
|
||||
"Are you sure you want to delete this sub-account?": "您確定要刪除這個子帳戶?",
|
||||
"Unable to delete this account": "無法刪除此帳戶",
|
||||
"Unable to delete this sub-account": "無法刪除此子帳戶",
|
||||
"Reconciliation Statement": "對帳單",
|
||||
"Transaction": "交易",
|
||||
"Transactions": "交易",
|
||||
"Transaction Pictures": "交易圖片",
|
||||
@@ -1804,6 +1807,7 @@
|
||||
"Failed to load image, please check whether the config \"domain\" and \"root_url\" are set correctly.": "無法載入圖片,請檢查設定 \"domain\" 和 \"root_url\" 是否設定正確。",
|
||||
"Search transaction description": "搜尋交易描述",
|
||||
"Unable to retrieve transaction list": "無法取得交易清單",
|
||||
"Unable to retrieve reconciliation statements": "無法取得對帳單",
|
||||
"Custom Date Range": "自訂日期範圍",
|
||||
"Transaction Detail": "交易明細",
|
||||
"No transaction data": "沒有交易資料",
|
||||
@@ -1823,6 +1827,8 @@
|
||||
"Total Liabilities": "總負債",
|
||||
"Total Expense": "總支出",
|
||||
"Total Income": "總收入",
|
||||
"Total Outflows": "總流出",
|
||||
"Total Inflows": "總流入",
|
||||
"Total Balance": "總結餘",
|
||||
"Expense By Account": "帳戶支出",
|
||||
"Expense By Primary Category": "一級分類支出",
|
||||
|
||||
@@ -518,6 +518,12 @@ export interface TransactionListInMonthByPageRequest {
|
||||
readonly keyword: string;
|
||||
}
|
||||
|
||||
export interface TransactionReconciliationStatementRequest {
|
||||
readonly accountId: string;
|
||||
readonly startTime: number;
|
||||
readonly endTime: number;
|
||||
}
|
||||
|
||||
export type TransactionGeoLocationResponse = Coordinate;
|
||||
|
||||
export interface TransactionInfoResponse {
|
||||
@@ -655,6 +661,14 @@ export interface TransactionInfoPageWrapperResponse2 {
|
||||
readonly totalCount: number;
|
||||
}
|
||||
|
||||
export interface TransactionReconciliationStatementResponseItem extends TransactionInfoResponse {
|
||||
readonly accountBalance: number;
|
||||
}
|
||||
|
||||
export interface TransactionReconciliationStatementResponse {
|
||||
readonly transactions: TransactionReconciliationStatementResponseItem[];
|
||||
}
|
||||
|
||||
export interface TransactionPageWrapper {
|
||||
readonly items: Transaction[];
|
||||
readonly totalCount?: number;
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type TransactionCreateRequest,
|
||||
type TransactionInfoResponse,
|
||||
type TransactionPageWrapper,
|
||||
type TransactionReconciliationStatementResponse,
|
||||
Transaction,
|
||||
EMPTY_TRANSACTION_RESULT
|
||||
} from '@/models/transaction.ts';
|
||||
@@ -961,6 +962,35 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function getReconciliationStatements({ accountId, startTime, endTime }: { accountId: string, startTime: number, endTime: number }): Promise<TransactionReconciliationStatementResponse> {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getReconciliationStatements({
|
||||
accountId: accountId,
|
||||
startTime: startTime,
|
||||
endTime: endTime
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to retrieve reconciliation statements' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load reconciliation statements', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to retrieve reconciliation statements' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getTransaction({ transactionId, withPictures }: { transactionId: string, withPictures?: boolean }): Promise<Transaction> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!isDefined(withPictures)) {
|
||||
@@ -1328,6 +1358,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
getExportTransactionDataRequestByTransactionFilter,
|
||||
loadTransactions,
|
||||
loadMonthlyAllTransactions,
|
||||
getReconciliationStatements,
|
||||
getTransaction,
|
||||
saveTransaction,
|
||||
deleteTransaction,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.ts';
|
||||
|
||||
import type { WeekDayValue } from '@/core/datetime.ts';
|
||||
import { type AccountCategory, AccountType } from '@/core/account.ts';
|
||||
import type { Account, CategorizedAccount } from '@/models/account.ts';
|
||||
|
||||
@@ -27,6 +28,8 @@ export function useAccountListPageBaseBase() {
|
||||
set: (value) => settingsStore.setShowAccountBalance(value)
|
||||
});
|
||||
|
||||
const firstDayOfWeek = computed<WeekDayValue>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const fiscalYearStart = computed<number>(() => userStore.currentUserFiscalYearStart);
|
||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||
|
||||
const allAccounts = computed<Account[]>(() => accountsStore.allAccounts);
|
||||
@@ -86,6 +89,8 @@ export function useAccountListPageBaseBase() {
|
||||
displayOrderModified,
|
||||
// computed states
|
||||
showAccountBalance,
|
||||
firstDayOfWeek,
|
||||
fiscalYearStart,
|
||||
defaultCurrency,
|
||||
allAccounts,
|
||||
allCategorizedAccountsMap,
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.ts';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import { TransactionType } from '@/core/transaction.ts';
|
||||
import type { Account } from '@/models/account.ts';
|
||||
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
||||
import type { TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
||||
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffsetMinutes,
|
||||
parseDateFromUnixTime,
|
||||
getUnixTime
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
export function useReconciliationStatementPageBase() {
|
||||
const {
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatAmountWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const userStore = useUserStore();
|
||||
const accountsStore = useAccountsStore();
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
|
||||
const accountId = ref<string>('');
|
||||
const startTime = ref<number>(0);
|
||||
const endTime = ref<number>(0);
|
||||
const reconciliationStatements = ref<TransactionReconciliationStatementResponseItem[]>([]);
|
||||
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||
|
||||
const allAccountsMap = computed<Record<string, Account>>(() => accountsStore.allAccountsMap);
|
||||
const allCategoriesMap = computed<Record<string, TransactionCategory>>(() => transactionCategoriesStore.allTransactionCategoriesMap);
|
||||
|
||||
const displayStartDateTime = computed<string>(() => {
|
||||
return formatUnixTimeToLongDateTime(startTime.value);
|
||||
});
|
||||
|
||||
const displayEndDateTime = computed<string>(() => {
|
||||
return formatUnixTimeToLongDateTime(endTime.value);
|
||||
});
|
||||
|
||||
const displayTotalOutflows = computed<string>(() => {
|
||||
let totalOutflows = 0;
|
||||
|
||||
for (let i = 0; i < reconciliationStatements.value.length; i++) {
|
||||
const transaction = reconciliationStatements.value[i];
|
||||
|
||||
if (transaction.type === TransactionType.Expense) {
|
||||
totalOutflows += transaction.sourceAmount;
|
||||
} else if (transaction.type === TransactionType.Transfer && transaction.sourceAccountId === accountId.value) {
|
||||
totalOutflows += transaction.sourceAmount;
|
||||
}
|
||||
}
|
||||
|
||||
let currency = defaultCurrency.value;
|
||||
|
||||
if (allAccountsMap.value[accountId.value]) {
|
||||
currency = allAccountsMap.value[accountId.value].currency;
|
||||
}
|
||||
|
||||
return formatAmountWithCurrency(totalOutflows, currency);
|
||||
});
|
||||
|
||||
const displayTotalInflows = computed<string>(() => {
|
||||
let totalInflows = 0;
|
||||
|
||||
for (let i = 0; i < reconciliationStatements.value.length; i++) {
|
||||
const transaction = reconciliationStatements.value[i];
|
||||
|
||||
if (transaction.type === TransactionType.Income) {
|
||||
totalInflows += transaction.sourceAmount;
|
||||
} else if (transaction.type === TransactionType.Transfer && transaction.destinationAccountId === accountId.value) {
|
||||
totalInflows += transaction.destinationAmount;
|
||||
}
|
||||
}
|
||||
|
||||
let currency = defaultCurrency.value;
|
||||
|
||||
if (allAccountsMap.value[accountId.value]) {
|
||||
currency = allAccountsMap.value[accountId.value].currency;
|
||||
}
|
||||
|
||||
return formatAmountWithCurrency(totalInflows, currency);
|
||||
});
|
||||
|
||||
function getDisplayDateTime(transaction: TransactionReconciliationStatementResponseItem): string {
|
||||
const transactionTime = getUnixTime(parseDateFromUnixTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value));
|
||||
return formatUnixTimeToLongDateTime(transactionTime);
|
||||
}
|
||||
|
||||
function getDisplayTimezone(transaction: TransactionReconciliationStatementResponseItem): string {
|
||||
return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.utcOffset)}`;
|
||||
}
|
||||
|
||||
function getDisplaySourceAmount(transaction: TransactionReconciliationStatementResponseItem): string {
|
||||
let currency = defaultCurrency.value;
|
||||
|
||||
if (allAccountsMap.value[transaction.sourceAccountId]) {
|
||||
currency = allAccountsMap.value[transaction.sourceAccountId].currency;
|
||||
}
|
||||
|
||||
return formatAmountWithCurrency(transaction.sourceAmount, currency);
|
||||
}
|
||||
|
||||
function getDisplayDestinationAmount(transaction: TransactionReconciliationStatementResponseItem): string {
|
||||
let currency = defaultCurrency.value;
|
||||
|
||||
if (allAccountsMap.value[transaction.destinationAccountId]) {
|
||||
currency = allAccountsMap.value[transaction.destinationAccountId].currency;
|
||||
}
|
||||
|
||||
return formatAmountWithCurrency(transaction.destinationAmount, currency);
|
||||
}
|
||||
|
||||
function getDisplayAccountBalance(transaction: TransactionReconciliationStatementResponseItem): string {
|
||||
let currency = defaultCurrency.value;
|
||||
let isLiabilityAccount = false;
|
||||
|
||||
if (transaction.type === TransactionType.Transfer && transaction.destinationAccountId === accountId.value) {
|
||||
if (allAccountsMap.value[transaction.destinationAccountId]) {
|
||||
currency = allAccountsMap.value[transaction.destinationAccountId].currency;
|
||||
isLiabilityAccount = allAccountsMap.value[transaction.destinationAccountId].isLiability;
|
||||
}
|
||||
} else if (allAccountsMap.value[transaction.sourceAccountId]) {
|
||||
currency = allAccountsMap.value[transaction.sourceAccountId].currency;
|
||||
isLiabilityAccount = allAccountsMap.value[transaction.sourceAccountId].isLiability;
|
||||
}
|
||||
|
||||
if (isLiabilityAccount) {
|
||||
return formatAmountWithCurrency(-transaction.accountBalance, currency);
|
||||
} else {
|
||||
return formatAmountWithCurrency(transaction.accountBalance, currency);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// states
|
||||
accountId,
|
||||
startTime,
|
||||
endTime,
|
||||
reconciliationStatements,
|
||||
// computed states
|
||||
currentTimezoneOffsetMinutes,
|
||||
defaultCurrency,
|
||||
allAccountsMap,
|
||||
allCategoriesMap,
|
||||
displayStartDateTime,
|
||||
displayEndDateTime,
|
||||
displayTotalOutflows,
|
||||
displayTotalInflows,
|
||||
// functions
|
||||
getDisplayDateTime,
|
||||
getDisplayTimezone,
|
||||
getDisplaySourceAmount,
|
||||
getDisplayDestinationAmount,
|
||||
getDisplayAccountBalance
|
||||
};
|
||||
}
|
||||
@@ -211,6 +211,28 @@
|
||||
:to="`/transaction/list?accountIds=${element.getAccountOrSubAccountId(activeSubAccount[element.id])}`">
|
||||
{{ tt('Transaction List') }}
|
||||
</v-btn>
|
||||
<v-btn class="px-2 ml-1" density="comfortable" color="default" variant="text"
|
||||
:disabled="loading" :prepend-icon="mdiInvoiceListOutline"
|
||||
@click="showReconciliationStatementCustomDateRangeDialog(element.getAccountOrSubAccount(activeSubAccount[element.id]))"
|
||||
v-if="element.type === AccountType.SingleAccount.type || element.getSubAccount(activeSubAccount[element.id])">
|
||||
{{ tt('Reconciliation Statement') }}
|
||||
<v-menu activator="parent" :open-on-hover="true">
|
||||
<v-list>
|
||||
<template :key="dateRange.type"
|
||||
v-for="dateRange in accountReconciliationStatementDateRangs(element.getAccountOrSubAccount(activeSubAccount[element.id]))">
|
||||
<v-list-item class="text-sm" density="compact"
|
||||
:value="dateRange.type">
|
||||
<v-list-item-title class="cursor-pointer"
|
||||
@click="showReconciliationStatementCustomDateRangeDialog(element.getAccountOrSubAccount(activeSubAccount[element.id]), dateRange.type)">
|
||||
<div class="d-flex align-center">
|
||||
<span class="text-sm ml-3">{{ dateRange.displayName }}</span>
|
||||
</div>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
<v-btn class="px-2 ml-1" density="comfortable" color="default" variant="text"
|
||||
:class="{ 'd-none': loading, 'hover-display': !loading }"
|
||||
:disabled="loading"
|
||||
@@ -258,6 +280,13 @@
|
||||
</v-dialog>
|
||||
|
||||
<edit-dialog ref="editDialog" />
|
||||
<reconciliation-statement-dialog ref="reconciliationStatementDialog"
|
||||
@error="onShowDateRangeError" />
|
||||
|
||||
<date-range-selection-dialog :title="tt('Custom Date Range')"
|
||||
v-model:show="showCustomDateRangeDialog"
|
||||
@dateRange:change="onCustomDateRangeChanged"
|
||||
@error="onShowDateRangeError" />
|
||||
|
||||
<confirm-dialog ref="confirmDialog"/>
|
||||
<snack-bar ref="snackbar" />
|
||||
@@ -267,6 +296,7 @@
|
||||
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
||||
import SnackBar from '@/components/desktop/SnackBar.vue';
|
||||
import EditDialog from './list/dialogs/EditDialog.vue';
|
||||
import ReconciliationStatementDialog from './list/dialogs/ReconciliationStatementDialog.vue';
|
||||
import AccountFilterSettingsCard from '@/views/desktop/common/cards/AccountFilterSettingsCard.vue';
|
||||
|
||||
import { ref, computed, useTemplateRef, watch } from 'vue';
|
||||
@@ -277,9 +307,13 @@ import { useAccountListPageBaseBase } from '@/views/base/accounts/AccountListPag
|
||||
|
||||
import { useAccountsStore } from '@/stores/account.ts';
|
||||
|
||||
import { DateRange, DateRangeScene, type LocalizedDateRange, type TimeRangeAndDateType } from '@/core/datetime.ts';
|
||||
import { AccountType, AccountCategory } from '@/core/account.ts';
|
||||
import type { Account } from '@/models/account.ts';
|
||||
|
||||
import { isNumber } from '@/lib/common.ts';
|
||||
import { getDateRangeByDateType, getDateRangeByBillingCycleDateType } from '@/lib/datetime.ts';
|
||||
|
||||
import {
|
||||
mdiEyeOutline,
|
||||
mdiEyeOffOutline,
|
||||
@@ -290,6 +324,7 @@ import {
|
||||
mdiPencilOutline,
|
||||
mdiDeleteOutline,
|
||||
mdiListBoxOutline,
|
||||
mdiInvoiceListOutline,
|
||||
mdiDrag,
|
||||
mdiDotsVertical
|
||||
} from '@mdi/js';
|
||||
@@ -297,16 +332,19 @@ import {
|
||||
type ConfirmDialogType = InstanceType<typeof ConfirmDialog>;
|
||||
type SnackBarType = InstanceType<typeof SnackBar>;
|
||||
type EditDialogType = InstanceType<typeof EditDialog>;
|
||||
type ReconciliationStatementDialogType = InstanceType<typeof ReconciliationStatementDialog>;
|
||||
|
||||
const display = useDisplay();
|
||||
|
||||
const { tt, getCurrencyName, joinMultiText } = useI18n();
|
||||
const { tt, getAllDateRanges, getCurrencyName, joinMultiText } = useI18n();
|
||||
|
||||
const {
|
||||
loading,
|
||||
showHidden,
|
||||
displayOrderModified,
|
||||
showAccountBalance,
|
||||
firstDayOfWeek,
|
||||
fiscalYearStart,
|
||||
allAccounts,
|
||||
allCategorizedAccountsMap,
|
||||
allAccountCount,
|
||||
@@ -322,13 +360,16 @@ const accountsStore = useAccountsStore();
|
||||
const confirmDialog = useTemplateRef<ConfirmDialogType>('confirmDialog');
|
||||
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
||||
const editDialog = useTemplateRef<EditDialogType>('editDialog');
|
||||
const reconciliationStatementDialog = useTemplateRef<ReconciliationStatementDialogType>('reconciliationStatementDialog');
|
||||
|
||||
const activeAccountCategoryType = ref<number>(AccountCategory.Default.type);
|
||||
const activeTab = ref<string>('accountPage');
|
||||
const activeSubAccount = ref<Record<string, string>>({});
|
||||
const accountToShowReconciliationStatement = ref<Account | null>(null);
|
||||
const alwaysShowNav = ref<boolean>(display.mdAndUp.value);
|
||||
const showNav = ref<boolean>(display.mdAndUp.value);
|
||||
const showAccountsIncludedInTotalDialog = ref<boolean>(false);
|
||||
const showCustomDateRangeDialog = ref<boolean>(false);
|
||||
|
||||
const hasAnyVisibleAccount = computed<boolean>(() => accountsStore.allVisibleAccountsCount > 0);
|
||||
const activeAccountCategory = computed<AccountCategory | undefined>(() => AccountCategory.valueOf(activeAccountCategoryType.value));
|
||||
@@ -407,6 +448,10 @@ function accountCurrency(account: Account): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
function accountReconciliationStatementDateRangs(account: Account): LocalizedDateRange[] {
|
||||
return getAllDateRanges(DateRangeScene.Normal, true, !!accountsStore.getAccountStatementDate(account.id));
|
||||
}
|
||||
|
||||
function add(): void {
|
||||
editDialog.value?.open({
|
||||
category: activeAccountCategoryType.value
|
||||
@@ -440,6 +485,32 @@ function edit(account: Account): void {
|
||||
});
|
||||
}
|
||||
|
||||
function showReconciliationStatementCustomDateRangeDialog(account: Account, dateRangeType?: number): void {
|
||||
if (!isNumber(dateRangeType) || dateRangeType === DateRange.Custom.type) {
|
||||
accountToShowReconciliationStatement.value = account;
|
||||
showCustomDateRangeDialog.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let dateRange: TimeRangeAndDateType | null = null;
|
||||
|
||||
if (DateRange.isBillingCycle(dateRangeType)) {
|
||||
dateRange = getDateRangeByBillingCycleDateType(dateRangeType, firstDayOfWeek.value, fiscalYearStart.value, accountsStore.getAccountStatementDate(account.id));
|
||||
} else {
|
||||
dateRange = getDateRangeByDateType(dateRangeType, firstDayOfWeek.value, fiscalYearStart.value);
|
||||
}
|
||||
|
||||
if (!dateRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
reconciliationStatementDialog.value?.open({
|
||||
accountId: account.id,
|
||||
startTime: dateRange.minTime,
|
||||
endTime: dateRange.maxTime
|
||||
});
|
||||
}
|
||||
|
||||
function hide(account: Account, targetAccount: Account, hidden: boolean): void {
|
||||
loading.value = true;
|
||||
|
||||
@@ -548,6 +619,27 @@ function onMove(event: { moved: { element: { id: string }, oldIndex: number, new
|
||||
});
|
||||
}
|
||||
|
||||
function onCustomDateRangeChanged(minUnixTime: number, maxUnixTime: number): void {
|
||||
if (!accountToShowReconciliationStatement.value) {
|
||||
snackbar.value?.showMessage('An error occurred');
|
||||
return;
|
||||
}
|
||||
|
||||
showCustomDateRangeDialog.value = false;
|
||||
|
||||
reconciliationStatementDialog.value?.open({
|
||||
accountId: accountToShowReconciliationStatement.value.id,
|
||||
startTime: minUnixTime,
|
||||
endTime: maxUnixTime
|
||||
});
|
||||
|
||||
accountToShowReconciliationStatement.value = null;
|
||||
}
|
||||
|
||||
function onShowDateRangeError(message: string): void {
|
||||
snackbar.value?.showError(message);
|
||||
}
|
||||
|
||||
watch(() => display.mdAndUp.value, (newValue) => {
|
||||
alwaysShowNav.value = newValue;
|
||||
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<v-dialog :min-height="loading ? 600 : 400" :persistent="loading" v-model="showState">
|
||||
<v-card class="pa-6 pa-sm-10 pa-md-12">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<h4 class="text-h4">{{ tt('Reconciliation Statement') }}</h4>
|
||||
<v-progress-circular indeterminate size="22" class="ml-2" v-if="loading"></v-progress-circular>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #subtitle>
|
||||
<div class="text-body-1 text-center text-wrap mt-2">
|
||||
<span>{{ displayStartDateTime }}</span>
|
||||
<span> - </span>
|
||||
<span>{{ displayEndDateTime }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-data-table
|
||||
fixed-header
|
||||
fixed-footer
|
||||
multi-sort
|
||||
density="compact"
|
||||
item-value="index"
|
||||
:class="{ 'disabled': loading }"
|
||||
:headers="dataTableHeaders"
|
||||
:items="reconciliationStatements"
|
||||
:no-data-text="loading ? '' : tt('No transaction data')"
|
||||
v-model:items-per-page="countPerPage"
|
||||
v-model:page="currentPage"
|
||||
>
|
||||
<template #item.time="{ item }">
|
||||
<span>{{ getDisplayDateTime(item) }}</span>
|
||||
<v-chip class="ml-1" variant="flat" color="secondary" size="x-small"
|
||||
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.type="{ item }">
|
||||
<v-chip label color="secondary" variant="outlined" size="x-small" v-if="item.type === TransactionType.ModifyBalance">{{ tt('Modify Balance') }}</v-chip>
|
||||
<v-chip label class="text-income" variant="outlined" size="x-small" v-else-if="item.type === TransactionType.Income">{{ tt('Income') }}</v-chip>
|
||||
<v-chip label class="text-expense" variant="outlined" size="x-small" v-else-if="item.type === TransactionType.Expense">{{ tt('Expense') }}</v-chip>
|
||||
<v-chip label color="primary" variant="outlined" size="x-small" v-else-if="item.type === TransactionType.Transfer && item.destinationAccountId === accountId">{{ tt('Transfer In') }}</v-chip>
|
||||
<v-chip label color="primary" variant="outlined" size="x-small" v-else-if="item.type === TransactionType.Transfer && item.sourceAccountId === accountId">{{ tt('Transfer Out') }}</v-chip>
|
||||
<v-chip label color="primary" variant="outlined" size="x-small" v-else-if="item.type === TransactionType.Transfer">{{ tt('Transfer') }}</v-chip>
|
||||
<v-chip label color="default" variant="outlined" size="x-small" v-else>{{ tt('Unknown') }}</v-chip>
|
||||
</template>
|
||||
<template #item.categoryId="{ item }">
|
||||
<div class="d-flex align-center">
|
||||
<span v-if="item.type === TransactionType.ModifyBalance">-</span>
|
||||
<ItemIcon size="24px" icon-type="category"
|
||||
:icon-id="allCategoriesMap[item.categoryId].icon"
|
||||
:color="allCategoriesMap[item.categoryId].color"
|
||||
v-if="item.type !== TransactionType.ModifyBalance && allCategoriesMap[item.categoryId]"></ItemIcon>
|
||||
<span class="ml-2" v-if="item.type !== TransactionType.ModifyBalance && allCategoriesMap[item.categoryId]">
|
||||
{{ allCategoriesMap[item.categoryId].name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.sourceAmount="{ item }">
|
||||
<span>{{ getDisplaySourceAmount(item) }}</span>
|
||||
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId && getDisplaySourceAmount(item) !== getDisplayDestinationAmount(item)"></v-icon>
|
||||
<span v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId && getDisplaySourceAmount(item) !== getDisplayDestinationAmount(item)">{{ getDisplayDestinationAmount(item) }}</span>
|
||||
</template>
|
||||
<template #item.sourceAccountId="{ item }">
|
||||
<div class="d-flex align-center">
|
||||
<span v-if="item.sourceAccountId && allAccountsMap[item.sourceAccountId]">{{ allAccountsMap[item.sourceAccountId].name }}</span>
|
||||
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer"></v-icon>
|
||||
<span v-if="item.type === TransactionType.Transfer && item.destinationAccountId && allAccountsMap[item.destinationAccountId]">{{ allAccountsMap[item.destinationAccountId].name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.accountBalance="{ item }">
|
||||
<span>{{ getDisplayAccountBalance(item) }}</span>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<div class="title-and-toolbar d-flex align-center text-no-wrap mt-2">
|
||||
<span class="ml-2">{{ tt('Total Inflows') }}</span>
|
||||
<span class="text-income" v-if="loading">
|
||||
<v-skeleton-loader type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-income ml-2" v-else-if="!loading">
|
||||
{{ displayTotalInflows }}
|
||||
</span>
|
||||
<span class="ml-3">{{ tt('Total Outflows') }}</span>
|
||||
<span class="text-expense" v-if="loading">
|
||||
<v-skeleton-loader type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-expense ml-2" v-else-if="!loading">
|
||||
{{ displayTotalOutflows }}
|
||||
</span>
|
||||
<v-spacer/>
|
||||
<span v-if="reconciliationStatements && reconciliationStatements.length > 10">
|
||||
{{ tt('Transactions Per Page') }}
|
||||
</span>
|
||||
<v-select class="ml-2" density="compact" max-width="100"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
:disabled="loading"
|
||||
:items="reconciliationStatementsTablePageOptions"
|
||||
v-model="countPerPage"
|
||||
v-if="reconciliationStatements && reconciliationStatements.length > 10"
|
||||
/>
|
||||
<pagination-buttons density="compact"
|
||||
:disabled="loading"
|
||||
:totalPageCount="totalPageCount"
|
||||
v-model="currentPage"
|
||||
v-if="reconciliationStatements && reconciliationStatements.length > 10">
|
||||
</pagination-buttons>
|
||||
</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||
<v-btn color="secondary" variant="tonal"
|
||||
:disabled="loading" @click="close">{{ tt('Close') }}</v-btn>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import PaginationButtons from '@/components/desktop/PaginationButtons.vue';
|
||||
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
import { useReconciliationStatementPageBase } from '@/views/base/transactions/ReconciliationStatementPageBase.ts';
|
||||
|
||||
import { useAccountsStore } from '@/stores/account.ts';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.ts';
|
||||
|
||||
import { TransactionType } from '@/core/transaction.ts';
|
||||
|
||||
import {
|
||||
mdiArrowRight
|
||||
} from '@mdi/js';
|
||||
|
||||
interface ReconciliationStatementDialogTablePageOption {
|
||||
value: number;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'error', message: string): void;
|
||||
}>();
|
||||
|
||||
const { tt } = useI18n();
|
||||
|
||||
const {
|
||||
accountId,
|
||||
startTime,
|
||||
endTime,
|
||||
reconciliationStatements,
|
||||
currentTimezoneOffsetMinutes,
|
||||
allAccountsMap,
|
||||
allCategoriesMap,
|
||||
displayStartDateTime,
|
||||
displayEndDateTime,
|
||||
displayTotalOutflows,
|
||||
displayTotalInflows,
|
||||
getDisplayDateTime,
|
||||
getDisplayTimezone,
|
||||
getDisplaySourceAmount,
|
||||
getDisplayDestinationAmount,
|
||||
getDisplayAccountBalance
|
||||
} = useReconciliationStatementPageBase();
|
||||
|
||||
const accountsStore = useAccountsStore();
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
const transactionsStore = useTransactionsStore();
|
||||
|
||||
const showState = ref<boolean>(false);
|
||||
const loading = ref<boolean>(false);
|
||||
const currentPage = ref<number>(1);
|
||||
const countPerPage = ref<number>(10);
|
||||
|
||||
let rejectFunc: ((reason?: unknown) => void) | null = null;
|
||||
|
||||
const account = computed(() => allAccountsMap.value[accountId.value]);
|
||||
const reconciliationStatementsTablePageOptions = computed<ReconciliationStatementDialogTablePageOption[]>(() => getTablePageOptions(reconciliationStatements.value?.length));
|
||||
|
||||
const totalPageCount = computed<number>(() => {
|
||||
if (!reconciliationStatements.value || reconciliationStatements.value.length < 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < reconciliationStatements.value.length; i++) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return Math.ceil(count / countPerPage.value);
|
||||
});
|
||||
|
||||
const dataTableHeaders = computed<object[]>(() => {
|
||||
const headers: object[] = [];
|
||||
const accountBalanceName = account.value?.isLiability ? 'Account Outstanding Balance' : 'Account Balance';
|
||||
|
||||
headers.push({ key: 'time', value: 'time', title: tt('Transaction Time'), sortable: true, nowrap: true });
|
||||
headers.push({ key: 'type', value: 'type', title: tt('Type'), sortable: true, nowrap: true });
|
||||
headers.push({ key: 'categoryId', value: 'categoryId', title: tt('Category'), sortable: true, nowrap: true });
|
||||
headers.push({ key: 'sourceAmount', value: 'sourceAmount', title: tt('Amount'), sortable: true, nowrap: true });
|
||||
headers.push({ key: 'sourceAccountId', value: 'sourceAccountId', title: tt('Account'), sortable: true, nowrap: true });
|
||||
headers.push({ key: 'accountBalance', value: 'accountBalance', title: tt(accountBalanceName), sortable: true, nowrap: true });
|
||||
headers.push({ key: 'comment', value: 'comment', title: tt('Description'), sortable: true, nowrap: true });
|
||||
return headers;
|
||||
});
|
||||
|
||||
function getTablePageOptions(linesCount?: number): ReconciliationStatementDialogTablePageOption[] {
|
||||
const pageOptions: ReconciliationStatementDialogTablePageOption[] = [];
|
||||
|
||||
if (!linesCount || linesCount < 1) {
|
||||
pageOptions.push({ value: -1, title: tt('All') });
|
||||
return pageOptions;
|
||||
}
|
||||
|
||||
const availableCountPerPage = [ 5, 10, 15, 20, 25, 30, 50 ];
|
||||
|
||||
for (let i = 0; i < availableCountPerPage.length; i++) {
|
||||
const count = availableCountPerPage[i];
|
||||
|
||||
if (linesCount < count) {
|
||||
break;
|
||||
}
|
||||
|
||||
pageOptions.push({ value: count, title: count.toString() });
|
||||
}
|
||||
|
||||
pageOptions.push({ value: -1, title: tt('All') });
|
||||
|
||||
return pageOptions;
|
||||
}
|
||||
|
||||
function open(options: { accountId: string, startTime: number, endTime: number }): Promise<void> {
|
||||
accountId.value = options.accountId;
|
||||
startTime.value = options.startTime;
|
||||
endTime.value = options.endTime;
|
||||
reconciliationStatements.value = [];
|
||||
currentPage.value = 1;
|
||||
countPerPage.value = 10;
|
||||
showState.value = true;
|
||||
loading.value = true;
|
||||
|
||||
Promise.all([
|
||||
accountsStore.loadAllAccounts({ force: false }),
|
||||
transactionCategoriesStore.loadAllCategories({ force: false })
|
||||
]).then(() => {
|
||||
return transactionsStore.getReconciliationStatements({
|
||||
accountId: options.accountId,
|
||||
startTime: options.startTime,
|
||||
endTime: options.endTime
|
||||
});
|
||||
}).then(result => {
|
||||
reconciliationStatements.value = result.transactions;
|
||||
loading.value = false;
|
||||
}).catch(error => {
|
||||
loading.value = false;
|
||||
|
||||
if (!error.processed) {
|
||||
emit('error', error);
|
||||
showState.value = false;
|
||||
}
|
||||
})
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
rejectFunc = reject;
|
||||
});
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
rejectFunc?.();
|
||||
showState.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user