mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-14 06:57:35 +08:00
use the daylight saving time zone as default time zone rather than the current standard time zone during the DST
This commit is contained in:
+6
-6
@@ -150,10 +150,10 @@ func (a *AccountsApi) AccountCreateHandler(c *core.WebContext) (any, *errs.Error
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
_, utcOffset, err := c.GetClientTimezone()
|
||||
clientTimezone, _, err := c.GetClientTimezone()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[accounts.AccountCreateHandler] cannot get client timezone offset, because %s", err.Error())
|
||||
log.Warnf(c, "[accounts.AccountCreateHandler] cannot get client timezone, because %s", err.Error())
|
||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ func (a *AccountsApi) AccountCreateHandler(c *core.WebContext) (any, *errs.Error
|
||||
}
|
||||
}
|
||||
|
||||
err = a.accounts.CreateAccounts(c, mainAccount, accountCreateReq.BalanceTime, childrenAccounts, childrenAccountBalanceTimes, utcOffset)
|
||||
err = a.accounts.CreateAccounts(c, mainAccount, accountCreateReq.BalanceTime, childrenAccounts, childrenAccountBalanceTimes, clientTimezone)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[accounts.AccountCreateHandler] failed to create account \"id:%d\" for user \"uid:%d\", because %s", mainAccount.AccountId, uid, err.Error())
|
||||
@@ -315,10 +315,10 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
return nil, errs.ErrAccountIdInvalid
|
||||
}
|
||||
|
||||
_, utcOffset, err := c.GetClientTimezone()
|
||||
clientTimezone, _, err := c.GetClientTimezone()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] cannot get client timezone offset, because %s", err.Error())
|
||||
log.Warnf(c, "[accounts.AccountModifyHandler] cannot get client timezone, because %s", err.Error())
|
||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||
}
|
||||
|
||||
@@ -521,7 +521,7 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
||||
}
|
||||
}
|
||||
|
||||
err = a.accounts.ModifyAccounts(c, mainAccount, toUpdateAccounts, toAddAccounts, toAddAccountBalanceTimes, toDeleteAccountIds, utcOffset)
|
||||
err = a.accounts.ModifyAccounts(c, mainAccount, toUpdateAccounts, toAddAccounts, toAddAccountBalanceTimes, toDeleteAccountIds, clientTimezone)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[accounts.AccountModifyHandler] failed to update account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
||||
|
||||
@@ -302,11 +302,11 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
||||
return nil, "", errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
timezone, _, err := c.GetClientTimezone()
|
||||
clientTimezone, _, err := c.GetClientTimezone()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[data_managements.getExportedFileContent] cannot get client timezone offset, because %s", err.Error())
|
||||
timezone = time.Local
|
||||
log.Warnf(c, "[data_managements.getExportedFileContent] cannot get client timezone, because %s", err.Error())
|
||||
clientTimezone = time.Local
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
@@ -413,13 +413,13 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
||||
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
fileName := a.getFileName(user, timezone, fileType)
|
||||
fileName := a.getFileName(user, clientTimezone, fileType)
|
||||
|
||||
return result, fileName, nil
|
||||
}
|
||||
|
||||
func (a *DataManagementsApi) getFileName(user *models.User, timezone *time.Location, fileExtension string) string {
|
||||
currentTime := utils.FormatUnixTimeToLongDateTimeWithoutSecond(time.Now().Unix(), timezone)
|
||||
func (a *DataManagementsApi) getFileName(user *models.User, clientTimezone *time.Location, fileExtension string) string {
|
||||
currentTime := utils.FormatUnixTimeToLongDateTimeWithoutSecond(time.Now().Unix(), clientTimezone)
|
||||
currentTime = strings.Replace(currentTime, "-", "_", -1)
|
||||
currentTime = strings.Replace(currentTime, " ", "_", -1)
|
||||
currentTime = strings.Replace(currentTime, ":", "_", -1)
|
||||
|
||||
@@ -47,7 +47,7 @@ func (a *LargeLanguageModelsApi) RecognizeReceiptImageHandler(c *core.WebContext
|
||||
return nil, errs.ErrLargeLanguageModelProviderNotEnabled
|
||||
}
|
||||
|
||||
clientTimezone, utcOffset, err := c.GetClientTimezone()
|
||||
clientTimezone, _, err := c.GetClientTimezone()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[large_language_models.RecognizeReceiptImageHandler] cannot get client timezone, because %s", err.Error())
|
||||
@@ -238,10 +238,10 @@ func (a *LargeLanguageModelsApi) RecognizeReceiptImageHandler(c *core.WebContext
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
return a.parseRecognizedReceiptImageResponse(c, uid, utcOffset, result, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return a.parseRecognizedReceiptImageResponse(c, uid, clientTimezone, result, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
func (a *LargeLanguageModelsApi) parseRecognizedReceiptImageResponse(c *core.WebContext, uid int64, utcOffset int16, recognizedResult *models.RecognizedReceiptImageResult, accountMap map[string]*models.Account, expenseCategoryMap map[string]*models.TransactionCategory, incomeCategoryMap map[string]*models.TransactionCategory, transferCategoryMap map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (*models.RecognizedReceiptImageResponse, *errs.Error) {
|
||||
func (a *LargeLanguageModelsApi) parseRecognizedReceiptImageResponse(c *core.WebContext, uid int64, clientTimezone *time.Location, recognizedResult *models.RecognizedReceiptImageResult, accountMap map[string]*models.Account, expenseCategoryMap map[string]*models.TransactionCategory, incomeCategoryMap map[string]*models.TransactionCategory, transferCategoryMap map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (*models.RecognizedReceiptImageResponse, *errs.Error) {
|
||||
recognizedReceiptImageResponse := &models.RecognizedReceiptImageResponse{
|
||||
Type: models.TRANSACTION_TYPE_EXPENSE,
|
||||
}
|
||||
@@ -290,7 +290,7 @@ func (a *LargeLanguageModelsApi) parseRecognizedReceiptImageResponse(c *core.Web
|
||||
|
||||
if len(recognizedResult.Time) > 0 {
|
||||
longDateTime := a.getLongDateTime(recognizedResult.Time)
|
||||
timestamp, err := utils.ParseFromLongDateTime(longDateTime, utcOffset)
|
||||
timestamp, err := utils.ParseFromLongDateTimeInTimeZone(longDateTime, clientTimezone)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[large_language_models.parseRecognizedReceiptImageResponse] recoginzed time \"%s\" is invalid", recognizedResult.Time)
|
||||
|
||||
@@ -1488,10 +1488,10 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext)
|
||||
return nil, errs.ErrParameterInvalid
|
||||
}
|
||||
|
||||
_, utcOffset, err := c.GetClientTimezone()
|
||||
clientTimezone, _, err := c.GetClientTimezone()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[transactions.TransactionParseImportFileHandler] cannot get client timezone offset, because %s", err.Error())
|
||||
log.Warnf(c, "[transactions.TransactionParseImportFileHandler] cannot get client timezone, because %s", err.Error())
|
||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||
}
|
||||
|
||||
@@ -1688,7 +1688,7 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext)
|
||||
|
||||
tagMap := a.transactionTags.GetVisibleTagNameMapByList(tags)
|
||||
|
||||
parsedTransactions, _, _, _, _, _, err := dataImporter.ParseImportedData(c, user, fileData, utcOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
parsedTransactions, _, _, _, _, _, err := dataImporter.ParseImportedData(c, user, fileData, clientTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionParseImportFileHandler] failed to parse imported data for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||
|
||||
@@ -819,7 +819,7 @@ func (l *UserDataCli) ImportTransaction(c *core.CliContext, username string, fil
|
||||
return err
|
||||
}
|
||||
|
||||
parsedTransactions, newAccounts, newSubExpenseCategories, newSubIncomeCategories, newSubTransferCategories, newTags, err := dataImporter.ParseImportedData(c, user, data, utils.GetTimezoneOffsetMinutes(time.Local), converter.DefaultImporterOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
parsedTransactions, newAccounts, newSubExpenseCategories, newSubIncomeCategories, newSubTransferCategories, newTags, err := dataImporter.ParseImportedData(c, user, data, time.Local, converter.DefaultImporterOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
|
||||
if err != nil {
|
||||
log.CliErrorf(c, "[user_data.ImportTransaction] failed to parse imported data for \"%s\", because %s", username, err.Error())
|
||||
|
||||
@@ -2,6 +2,7 @@ package alipay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
@@ -53,7 +54,7 @@ type alipayTransactionDataCsvFileImporter struct {
|
||||
}
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the alipay transaction csv data
|
||||
func (c *alipayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *alipayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
enc := simplifiedchinese.GB18030
|
||||
reader := transform.NewReader(bytes.NewReader(data), enc.NewDecoder())
|
||||
|
||||
@@ -83,5 +84,5 @@ func (c *alipayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Contex
|
||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, alipayTransactionSupportedColumns, transactionRowParser)
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(alipayTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestAlipayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 4, len(allNewTransactions))
|
||||
@@ -113,7 +113,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testin
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||
@@ -133,7 +133,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testin
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||
@@ -164,7 +164,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvestmentRefundTransaction
|
||||
"2024-09-01 01:00:00,Test Account2,xxx-买入,不计收支,0.01,Test Account,退款成功,\n" +
|
||||
"2024-09-01 02:00:00,Test Account2,xxx-买入退款,不计收支,0.01,Test Account,退款成功,\n")
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(allNewTransactions))
|
||||
@@ -202,7 +202,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
data2, err := simplifiedchinese.GB18030.NewEncoder().String("支付宝交易记录明细查询\n" +
|
||||
@@ -214,7 +214,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -275,7 +275,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -291,7 +291,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -308,7 +308,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -325,7 +325,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -342,7 +342,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -359,7 +359,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data7), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data7), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -389,7 +389,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseCategory(t *testing.T) {
|
||||
"2024-09-01 23:59:59,Test Category3,充值-普通充值,不计收支,0.05,交易成功,\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 3, len(allNewTransactions))
|
||||
@@ -435,7 +435,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRelatedAccount(t *testing.T
|
||||
"2024-09-01 08:00:00,Test Account4,信用卡还款,不计收支,0.01,Test Account,还款成功,repayment,\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 9, len(allNewTransactions))
|
||||
@@ -547,7 +547,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -562,7 +562,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -587,7 +587,7 @@ func TestAlipayCsvFileImporterParseImportedData_SkipClosedIncomeOrTransferTransa
|
||||
"2024-09-01 23:59:59 ,充值-普通充值 ,0.05 ,不计收支 ,交易关闭 ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -608,7 +608,7 @@ func TestAlipayCsvFileImporterParseImportedData_SkipUnknownProductTransferTransa
|
||||
"2024-09-01 23:59:59 ,xxxx ,0.05 ,不计收支 ,交易成功 ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -629,7 +629,7 @@ func TestAlipayCsvFileImporterParseImportedData_SkipUnknownStatusTransaction(t *
|
||||
"2024-09-01 01:23:45 ,xxxx ,0.12 ,收入 ,xxxx ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -648,10 +648,10 @@ func TestAlipayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T)
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
}
|
||||
|
||||
@@ -672,7 +672,7 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
||||
"金额(元),收/支 ,交易状态 ,\n" +
|
||||
"0.12 ,收入 ,交易成功 ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Amount Column
|
||||
@@ -683,7 +683,7 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
||||
"交易创建时间 ,收/支 ,交易状态 ,\n" +
|
||||
"2024-09-01 12:34:56 ,收入 ,交易成功 ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Status Column
|
||||
@@ -694,7 +694,7 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
||||
"交易创建时间 ,金额(元),收/支 ,\n" +
|
||||
"2024-09-01 12:34:56 ,0.12 ,收入 ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Type Column
|
||||
@@ -705,7 +705,7 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
||||
"交易创建时间 ,金额(元),交易状态 ,\n" +
|
||||
"2024-09-01 12:34:56 ,0.12 ,交易成功 ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
}
|
||||
|
||||
@@ -724,6 +724,6 @@ func TestAlipayCsvFileImporterParseImportedData_NoTransactionData(t *testing.T)
|
||||
"---------------------------------交易记录明细列表------------------------------------\n" +
|
||||
"交易创建时间 ,金额(元),收/支 ,交易状态 ,\n" +
|
||||
"------------------------------------------------------------------------------------\n")
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package beancount
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -24,7 +26,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the Beancount transaction data
|
||||
func (c *beancountTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *beancountTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
beancountDataReader, err := createNewBeancountDataReader(ctx, data)
|
||||
|
||||
if err != nil {
|
||||
@@ -45,5 +47,5 @@ func (c *beancountTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
||||
|
||||
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(beancountTransactionTypeNameMapping, "", "", BEANCOUNT_TRANSACTION_TAG_SEPARATOR)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package beancount
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -33,7 +34,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData(t *testi
|
||||
" Expenses:TestCategory2 1.00 CNY\n"+
|
||||
"2024-09-04 *\n"+
|
||||
" Assets:TestAccount -0.05 CNY\n"+
|
||||
" Assets:TestAccount2 0.05 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount2 0.05 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -112,7 +113,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData2(t *test
|
||||
" Assets:TestAccount -1.00 CNY\n"+
|
||||
"2024-09-04 *\n"+
|
||||
" Assets:TestAccount2 0.05 CNY\n"+
|
||||
" Assets:TestAccount -0.05 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount -0.05 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -182,7 +183,7 @@ func TestBeancountTransactionDataFileParseImportedData_ParseInvalidTime(t *testi
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024/09/01 *\n"+
|
||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||
" Assets:TestAccount 123.45 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount 123.45 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -198,7 +199,7 @@ func TestBeancountTransactionDataFileParseImportedData_ParseValidCurrency(t *tes
|
||||
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||
" Assets:TestAccount -0.12 USD\n"+
|
||||
" Assets:TestAccount2 0.84 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount2 0.84 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -234,13 +235,13 @@ func TestBeancountTransactionDataFileParseImportedData_ParseInvalidAmount(t *tes
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 *\n"+
|
||||
" Equity:Opening-Balances -abc CNY\n"+
|
||||
" Assets:TestAccount abc CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount abc CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 *\n"+
|
||||
" Equity:Opening-Balances -1/0 CNY\n"+
|
||||
" Assets:TestAccount 1/0 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount 1/0 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -259,7 +260,7 @@ func TestBeancountTransactionDataFileParseImportedData_ParseDescription(t *testi
|
||||
" Assets:TestAccount 123.45 CNY\n"+
|
||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||
" Income:TestCategory -0.12 CNY\n"+
|
||||
" Assets:TestAccount 0.12 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount 0.12 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -281,25 +282,25 @@ func TestBeancountTransactionDataFileParseImportedData_InvalidTransaction(t *tes
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||
" Assets:TestAccount 0.11 CNY\n"+
|
||||
" Assets:TestAccount2 0.11 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||
" Expenses:TestCategory -0.11 CNY\n"+
|
||||
" Expenses:TestCategory2 0.11 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Expenses:TestCategory2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||
" Income:TestCategory -0.11 CNY\n"+
|
||||
" Income:TestCategory2 0.11 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Income:TestCategory2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||
" Equity:TestCategory -0.11 CNY\n"+
|
||||
" Equity:TestCategory2 0.11 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Equity:TestCategory2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
||||
}
|
||||
|
||||
@@ -316,7 +317,7 @@ func TestBeancountTransactionDataFileParseImportedData_NotSupportedToParseSplitT
|
||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||
" Assets:TestAccount -0.23 CNY\n"+
|
||||
" Assets:TestAccount2 0.11 CNY\n"+
|
||||
" Assets:TestAccount3 0.12 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount3 0.12 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||
}
|
||||
|
||||
@@ -333,27 +334,27 @@ func TestBeancountTransactionDataFileParseImportedData_MissingTransactionRequire
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"* \"narration\"\n"+
|
||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||
" Assets:TestAccount 123.45 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount 123.45 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
|
||||
// Missing Account Name
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 * \"narration\"\n"+
|
||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||
" 123.45 CNY\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" 123.45 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||
|
||||
// Missing Amount
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 * \"narration\"\n"+
|
||||
" Equity:Opening-Balances\n"+
|
||||
" Assets:TestAccount\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||
|
||||
// Missing Commodity
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 * \"narration\"\n"+
|
||||
" Equity:Opening-Balances -123.45\n"+
|
||||
" Assets:TestAccount 123.45\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
" Assets:TestAccount 123.45\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ func (t *camtStatementTransactionDataRowIterator) parseTransaction(ctx core.Cont
|
||||
}
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||
} else if entry.BookingDate != nil && entry.BookingDate.Date != "" {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = fmt.Sprintf("%s 00:00:00", entry.BookingDate.Date)
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = datatable.TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package camt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -23,7 +25,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the camt.053 file transaction data
|
||||
func (c *camt053TransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *camt053TransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
camt053DataReader, err := createNewCamt053FileReader(data)
|
||||
|
||||
if err != nil {
|
||||
@@ -44,5 +46,5 @@ func (c *camt053TransactionDataImporter) ParseImportedData(ctx core.Context, use
|
||||
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(camtTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package camt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -65,7 +66,7 @@ func TestCamt053TransactionDataFileParseImportedData_MinimumValidData(t *testing
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -158,7 +159,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseValidTransactionTime(t
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(allNewTransactions))
|
||||
@@ -197,7 +198,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -220,7 +221,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -243,7 +244,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -266,7 +267,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -315,7 +316,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(allNewTransactions))
|
||||
@@ -366,7 +367,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(allNewTransactions))
|
||||
@@ -404,7 +405,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -431,7 +432,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -468,7 +469,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmou
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -499,7 +500,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmou
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -530,7 +531,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmou
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -573,7 +574,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -608,7 +609,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -635,7 +636,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -665,7 +666,7 @@ func TestCamt053TransactionDataFileParseImportedData_MissingAccountNode(t *testi
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
||||
}
|
||||
|
||||
@@ -695,7 +696,7 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -717,7 +718,7 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -739,7 +740,7 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -761,6 +762,6 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -28,10 +28,11 @@ func (c *DataTableTransactionDataExporter) BuildExportedContent(ctx core.Context
|
||||
}
|
||||
|
||||
dataRowMap := make(map[datatable.TransactionDataTableColumn]string, 15)
|
||||
transactionUnixTime := utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime)
|
||||
transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
||||
|
||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime), transactionTimeZone)
|
||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(transactionTimeZone)
|
||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(transactionUnixTime, transactionTimeZone)
|
||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(transactionUnixTime, transactionTimeZone)
|
||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = dataTableBuilder.ReplaceDelimiters(c.getDisplayTransactionTypeName(transaction.Type))
|
||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_CATEGORY] = c.getExportedTransactionCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap)
|
||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = c.getExportedTransactionSubCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap)
|
||||
|
||||
@@ -3,6 +3,7 @@ package converter
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
@@ -29,7 +30,7 @@ type DataTableTransactionDataImporter struct {
|
||||
}
|
||||
|
||||
// ParseImportedData returns the imported transaction data
|
||||
func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, dataTable datatable.TransactionDataTable, defaultTimezoneOffset int16, additionalOptions TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, dataTable datatable.TransactionDataTable, defaultTimezone *time.Location, additionalOptions TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
if dataTable.TransactionRowCount() < 1 {
|
||||
log.Errorf(ctx, "[data_table_transaction_data_importer.ParseImportedData] cannot parse import data for user \"uid:%d\", because data table row count is less 1", user.Uid)
|
||||
return nil, nil, nil, nil, nil, nil, errs.ErrNotFoundTransactionDataInFile
|
||||
@@ -94,7 +95,7 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
||||
continue
|
||||
}
|
||||
|
||||
timezoneOffset := defaultTimezoneOffset
|
||||
timezone := defaultTimezone
|
||||
|
||||
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) &&
|
||||
dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) != datatable.TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE {
|
||||
@@ -105,10 +106,10 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
||||
return nil, nil, nil, nil, nil, nil, errs.ErrTransactionTimeZoneInvalid
|
||||
}
|
||||
|
||||
timezoneOffset = utils.GetTimezoneOffsetMinutes(transactionTimezone)
|
||||
timezone = transactionTimezone
|
||||
}
|
||||
|
||||
transactionTime, err := utils.ParseFromLongDateTime(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), timezoneOffset)
|
||||
transactionTime, err := utils.ParseFromLongDateTimeInTimeZone(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), timezone)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "[data_table_transaction_data_importer.ParseImportedData] cannot parse time \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), dataRowIndex, user.Uid, err.Error())
|
||||
@@ -373,7 +374,7 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
||||
Type: transactionDbType,
|
||||
CategoryId: categoryId,
|
||||
TransactionTime: utils.GetMinTransactionTimeFromUnixTime(transactionTime.Unix()),
|
||||
TimezoneUtcOffset: timezoneOffset,
|
||||
TimezoneUtcOffset: utils.GetTimezoneOffsetMinutes(transactionTime.Unix(), timezone),
|
||||
AccountId: account.AccountId,
|
||||
Amount: amount,
|
||||
HideAmount: false,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package converter
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
)
|
||||
@@ -14,7 +16,7 @@ type TransactionDataExporter interface {
|
||||
// TransactionDataImporter defines the structure of transaction data importer
|
||||
type TransactionDataImporter interface {
|
||||
// ParseImportedData returns the imported data
|
||||
ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error)
|
||||
ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error)
|
||||
}
|
||||
|
||||
// TransactionDataConverter defines the structure of transaction data converter
|
||||
|
||||
@@ -35,7 +35,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the transaction json data
|
||||
func (c *defaultTransactionDataJsonImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *defaultTransactionDataJsonImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
var importRequest models.ImportTransactionRequest
|
||||
|
||||
if err := json.Unmarshal(data, &importRequest); err != nil {
|
||||
@@ -55,7 +55,7 @@ func (c *defaultTransactionDataJsonImporter) ParseImportedData(ctx core.Context,
|
||||
ezbookkeepingTagSeparator,
|
||||
)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
func (c *defaultTransactionDataJsonImporter) createNewDefaultTransactionDataTable(importRequest models.ImportTransactionRequest) (datatable.TransactionDataTable, error) {
|
||||
@@ -75,10 +75,11 @@ func (c *defaultTransactionDataJsonImporter) createNewDefaultTransactionDataTabl
|
||||
}
|
||||
|
||||
timezone := time.FixedZone("Transaction Timezone", utcOffset*60)
|
||||
timezoneOffset := utils.FormatTimezoneOffset(time.Now().Unix(), timezone)
|
||||
|
||||
row := make(map[datatable.TransactionDataTableColumn]string, len(allJsonDataSupportedColumns))
|
||||
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = transaction.Time
|
||||
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(timezone)
|
||||
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = timezoneOffset
|
||||
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = transaction.Type
|
||||
row[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = transaction.CategoryName
|
||||
row[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = transaction.SourceAccountName
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package _default
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
@@ -84,7 +86,7 @@ func (c *defaultTransactionDataPlainTextConverter) ToExportedContent(ctx core.Co
|
||||
}
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the transaction plain text data
|
||||
func (c *defaultTransactionDataPlainTextConverter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *defaultTransactionDataPlainTextConverter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
dataTable, err := createNewDefaultPlainTextDataTable(
|
||||
string(data),
|
||||
c.columnSeparator,
|
||||
@@ -104,5 +106,5 @@ func (c *defaultTransactionDataPlainTextConverter) ParseImportedData(ctx core.Co
|
||||
ezbookkeepingTagSeparator,
|
||||
)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package _default
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -139,7 +140,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_MinimumValidDat
|
||||
"2024-09-01 00:00:00,Balance Modification,,Test Account,123.45,,\n"+
|
||||
"2024-09-01 01:23:45,Income,Test Category,Test Account,0.12,,\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category2,Test Account,1.00,,\n"+
|
||||
"2024-09-01 23:59:59,Transfer,Test Category3,Test Account,0.05,Test Account2,0.05"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 23:59:59,Transfer,Test Category3,Test Account,0.05,Test Account2,0.05"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -207,11 +208,11 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTim
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01T12:34:56,Expense,Test Category,Test Account,123.45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01T12:34:56,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"09/01/2024 12:34:56,Expense,Test Category,Test Account,123.45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"09/01/2024 12:34:56,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -225,7 +226,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTyp
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01 12:34:56,Type,Test Category,Test Account,123.45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Type,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -239,19 +240,19 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidTimez
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01 12:34:56,-10:00,Expense,Test Category,Test Account,123.45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,-10:00,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01 12:34:56,+00:00,Expense,Test Category,Test Account,123.45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,+00:00,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01 12:34:56,+12:45,Expense,Test Category,Test Account,123.45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,+12:45,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -267,7 +268,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTim
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01 12:34:56,Asia/Shanghai,Expense,Test Category,Test Account,123.45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Asia/Shanghai,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -282,7 +283,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidAccou
|
||||
|
||||
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
||||
"2024-09-01 12:34:56,Transfer,Test Category2,Test Account,USD,1.23,Test Account2,EUR,1.10"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Transfer,Test Category2,Test Account,USD,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -309,12 +310,12 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAcc
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
||||
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account,CNY,1.23,Test Account2,EUR,1.10"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account,CNY,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
||||
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account2,CNY,1.23,Test Account,EUR,1.10"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account2,CNY,1.23,Test Account,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -328,11 +329,11 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNotSupport
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,XXX,123.45,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,XXX,123.45,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||
"2024-09-01 01:23:45,Transfer,Test Category,Test Account,USD,123.45,Test Account2,XXX,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 01:23:45,Transfer,Test Category,Test Account,USD,123.45,Test Account2,XXX,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -346,11 +347,11 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAmo
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123 45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123 45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2,123 45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2,123 45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -364,14 +365,14 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNoAmount2(
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||
assert.Equal(t, int64(0), allNewTransactions[0].RelatedAccountAmount)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2\n"+
|
||||
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||
@@ -388,7 +389,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidGeogr
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,123.45 45.56"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,123.45 45.56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -406,18 +407,18 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidGeo
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLongitude)
|
||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLatitude)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,a b"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,a b"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1 "), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1 "), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -431,7 +432,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseTag(t *tes
|
||||
}
|
||||
|
||||
_, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Tags\n"+
|
||||
"2024-09-01 00:00:00,Balance Modification,,Test Account,123.45,,,foo;;bar.;#test;hello\tworld;;"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,Balance Modification,,Test Account,123.45,,,foo;;bar.;#test;hello\tworld;;"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -460,7 +461,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseDescriptio
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Description\n"+
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,foo bar\t#test"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,foo bar\t#test"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -476,7 +477,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingFileHead
|
||||
DefaultCurrency: "CNY",
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -491,31 +492,31 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingRequired
|
||||
|
||||
// Missing Time Column
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Timezone,Type,Category,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||
"+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Type Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Category,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||
"2024-09-01 00:00:00,+08:00,Test Category,Test Sub Category,Test Account,CNY,123.45,,,,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,+08:00,Test Category,Test Sub Category,Test Account,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Sub Category Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,Test Account,CNY,123.45,,,,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,Test Account,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Account Name Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,CNY,123.45,,,,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Amount Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account,Account Currency,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,,,,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Account2 Name Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account,Account Currency,Amount,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
@@ -148,7 +149,7 @@ func (c *customTransactionDataDsvFileImporter) ParseDsvFileLines(ctx core.Contex
|
||||
}
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the custom transaction dsv data
|
||||
func (c *customTransactionDataDsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *customTransactionDataDsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
allLines, err := c.ParseDsvFileLines(ctx, data)
|
||||
|
||||
if err != nil {
|
||||
@@ -159,7 +160,7 @@ func (c *customTransactionDataDsvFileImporter) ParseImportedData(ctx core.Contex
|
||||
transactionDataTable := CreateNewCustomPlainTextDataTable(dataTable, c.columnIndexMapping, c.transactionTypeNameMapping, c.timeFormat, c.timezoneFormat, c.amountDecimalSeparator, c.amountDigitGroupingSymbol)
|
||||
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(customTransactionTypeNameMapping, c.geoLocationSeparator, c.geoLocationOrder, c.transactionTagSeparator)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
// IsDelimiterSeparatedValuesFileType returns whether the file type is the delimiter-separated values file type
|
||||
|
||||
@@ -2,6 +2,7 @@ package dsv
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -92,7 +93,7 @@ func TestCustomTransactionDataDsvFileImporter_MinimumValidData(t *testing.T) {
|
||||
"2024-09-01 00:00:00,B,123.45\n"+
|
||||
"2024-09-01 01:23:45,I,0.12\n"+
|
||||
"2024-09-01 12:34:56,E,1.00\n"+
|
||||
"2024-09-01 23:59:59,T,0.05"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 23:59:59,T,0.05"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -184,7 +185,7 @@ func TestCustomTransactionDataDsvFileImporter_WithAllSupportedColumns(t *testing
|
||||
"\"2024-09-01 00:00:00\",\"+08:00\",\"Balance Modification\",\"\",\"\",\"Test Account\",\"CNY\",\"123.45\",\"\",\"\",\"\",\"\",\"\",\"\"\n"+
|
||||
"\"2024-09-01 01:23:45\",\"+08:00\",\"Income\",\"Test Category\",\"Test Sub Category\",\"Test Account\",\"CNY\",\"0.12\",\"\",\"\",\"\",\"123.450000 45.670000\",\"Test Tag;Test Tag2\",\"Hello World\"\n"+
|
||||
"\"2024-09-01 12:34:56\",\"+00:00\",\"Expense\",\"Test Category2\",\"Test Sub Category2\",\"Test Account\",\"CNY\",\"1.00\",\"\",\"\",\"\",\"\",\"Test Tag\",\"Foo#Bar\"\n"+
|
||||
"\"2024-09-01 23:59:59\",\"-05:00\",\"Transfer\",\"Test Category3\",\"Test Sub Category3\",\"Test Account\",\"CNY\",\"0.05\",\"Test Account2\",\"USD\",\"0.35\",\"\",\"Test Tag2\",\"foo\tbar\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"2024-09-01 23:59:59\",\"-05:00\",\"Transfer\",\"Test Category3\",\"Test Sub Category3\",\"Test Account\",\"CNY\",\"0.05\",\"Test Account2\",\"USD\",\"0.35\",\"\",\"Test Tag2\",\"foo\tbar\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -273,11 +274,11 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTime(t *testing.T) {
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01T12:34:56,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01T12:34:56,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"09/01/2024 12:34:56,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"09/01/2024 12:34:56,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -304,7 +305,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTransactionWithoutType(t *tes
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,A,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,A,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -328,7 +329,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidType(t *testing.T) {
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,B,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,B,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -352,19 +353,19 @@ func TestCustomTransactionDataDsvFileImporter_ParseTimeWithTimezone(t *testing.T
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56-10:00,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56-10:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56+00:00,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56+00:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56+12:45,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56+12:45,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -390,19 +391,19 @@ func TestCustomTransactionDataDsvFileImporter_ParseTimeWithTimezone2(t *testing.
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56-1000,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56-1000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56+0000,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56+0000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56+1245,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56+1245,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -429,19 +430,19 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidTimezone(t *testing.T) {
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,-10:00,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,-10:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,+00:00,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,+00:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,+12:45,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,+12:45,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -468,19 +469,19 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidTimezone2(t *testing.T)
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,-1000,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,-1000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,+0000,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,+0000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,+1245,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,+1245,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -507,7 +508,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezoneFormat(t *test
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,CST,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,CST,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrImportFileTransactionTimezoneFormatInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -532,11 +533,11 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezone(t *testing.T)
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,-0700,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,-0700,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -561,11 +562,11 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezone2(t *testing.T
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,0700,E,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,0700,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -589,7 +590,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseAmountWithCustomFormat(t *tes
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56\tE\t1.234,56"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56\tE\t1.234,56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(123456), allNewTransactions[0].Amount)
|
||||
@@ -615,7 +616,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmountWithCustomFormat
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56\tE\t1.234,56"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56\tE\t1.234,56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -639,7 +640,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmountWithCustomFormat
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56\tE\t1.234,56"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56\tE\t1.234,56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -670,7 +671,7 @@ func TestCustomTransactionDataDsvFileImporter_ParsePrimaryCategory(t *testing.T)
|
||||
"2024-09-01 00:00:00,B,,123.45\n"+
|
||||
"2024-09-01 01:23:45,I,Test Category,0.12\n"+
|
||||
"2024-09-01 12:34:56,E,Test Category2,1.00\n"+
|
||||
"2024-09-01 23:59:59,T,Test Category3,0.05"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 23:59:59,T,Test Category3,0.05"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -737,7 +738,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidAccountCurrency(t *testi
|
||||
|
||||
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
||||
"2024-09-01 12:34:56,T,Test Account,USD,1.23,Test Account2,EUR,1.10"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,T,Test Account,USD,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -780,12 +781,12 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAccountCurrency(t *tes
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
||||
"2024-09-01 12:34:56,T,Test Account,CNY,1.23,Test Account2,EUR,1.10"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,T,Test Account,CNY,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
||||
"2024-09-01 12:34:56,T,Test Account2,CNY,1.23,Test Account,EUR,1.10"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,T,Test Account2,CNY,1.23,Test Account,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -815,11 +816,11 @@ func TestCustomTransactionDataDsvFileImporter_ParseNotSupportedCurrency(t *testi
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 01:23:45,B,Test Account,XXX,123.45,,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 01:23:45,B,Test Account,XXX,123.45,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 01:23:45,T,Test Account,USD,123.45,Test Account2,XXX,123.45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 01:23:45,T,Test Account,USD,123.45,Test Account2,XXX,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -850,7 +851,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidAmount(t *testing.T) {
|
||||
"2024-09-01 00:00:00,B,123.45000000,\n"+
|
||||
"2024-09-01 01:23:45,I,0.12000000,\n"+
|
||||
"2024-09-01 12:34:56,E,1.00000000,\n"+
|
||||
"2024-09-01 23:59:59,T,0.05000000,0.35000000"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 23:59:59,T,0.05000000,0.35000000"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -895,7 +896,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseAmountWithSpaceDigitGroupingS
|
||||
|
||||
// normal space
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -904,7 +905,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseAmountWithSpaceDigitGroupingS
|
||||
|
||||
// no-break space (NBSP)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -913,7 +914,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseAmountWithSpaceDigitGroupingS
|
||||
|
||||
// narrow no-break space (NNBSP)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -922,7 +923,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseAmountWithSpaceDigitGroupingS
|
||||
|
||||
// figure space
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -954,11 +955,11 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmount(t *testing.T) {
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,E,Test Account,123 45,,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,E,Test Account,123 45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2,123 45"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2,123 45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -985,14 +986,14 @@ func TestCustomTransactionDataDsvFileImporter_ParseNoAmount2(t *testing.T) {
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,E,Test Account,123.45,"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,E,Test Account,123.45,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||
assert.Equal(t, int64(0), allNewTransactions[0].RelatedAccountAmount)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||
@@ -1020,7 +1021,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidGeographicLocation(t *te
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,E,123.45,123.45;45.56"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,E,123.45,123.45;45.56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -1049,14 +1050,14 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidGeographicLocation(t *
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,E,123.45,,,1"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,E,123.45,,,1"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLongitude)
|
||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLatitude)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,E,123.45,a b"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,E,123.45,a b"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -1081,7 +1082,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTag(t *testing.T) {
|
||||
}
|
||||
|
||||
_, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -1121,7 +1122,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTagWithoutSeparator(t *testin
|
||||
}
|
||||
|
||||
_, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -1152,7 +1153,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseDescription(t *testing.T) {
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"2024-09-01 12:34:56,T,123.45,foo bar\t#test"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"2024-09-01 12:34:56,T,123.45,foo bar\t#test"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
|
||||
@@ -147,7 +147,7 @@ func (t *customPlainTextDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||
|
||||
if t.transactionDataTable.timeFormatIncludeTimezone {
|
||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package feidee
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
@@ -61,7 +62,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the feidee mymoney app transaction csv data
|
||||
func (c *feideeMymoneyAppTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *feideeMymoneyAppTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
fallback := unicode.UTF8.NewDecoder()
|
||||
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
||||
|
||||
@@ -97,7 +98,7 @@ func (c *feideeMymoneyAppTransactionDataCsvFileImporter) ParseImportedData(ctx c
|
||||
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
func (c *feideeMymoneyAppTransactionDataCsvFileImporter) createNewFeideeMymoneyAppTransactionDataTable(ctx core.Context, commonDataTable datatable.CommonDataTable) (datatable.TransactionDataTable, error) {
|
||||
|
||||
+29
-29
@@ -31,7 +31,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_MinimumValidData(t *testi
|
||||
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account2\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\"\n"+
|
||||
"\"转出\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account2\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转出\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account2\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -122,7 +122,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseOutstandingBalanceMo
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"负债变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"+
|
||||
"\"负债变更\",\"2024-09-01 01:00:00\",\"\",\"Test Account2\",\"-0.12\",\"\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"负债变更\",\"2024-09-01 01:00:00\",\"\",\"Test Account2\",\"-0.12\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -171,12 +171,12 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidTime(t *testi
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"收入\",\"2024-09-01T12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"收入\",\"2024-09-01T12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"收入\",\"09/01/2024 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"收入\",\"09/01/2024 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidType(t *testi
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"Type\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"Type\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseValidAccountCurrency
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"USD\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -237,14 +237,14 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAccountCurren
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"CNY\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"CNY\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -259,19 +259,19 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseNotSupportedCurrency
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"XXX\",\"123.45\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"XXX\",\"123.45\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"USD\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"XXX\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"XXX\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"XXX\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"USD\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"USD\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -286,24 +286,24 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAmount(t *tes
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"负债变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"负债变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123 45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123 45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123 45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseDescription(t *testi
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"Test\n"+
|
||||
"A new line break\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"A new line break\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -337,7 +337,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_WithAdditionalOptions(t *
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\",\"成员\",\"项目\",\"商家\"\n"+
|
||||
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\",\"test1\",\"test2\",\"test3\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\",\"test1\",\"test2\",\"test3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -345,7 +345,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_WithAdditionalOptions(t *
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\",\"成员\",\"项目\",\"商家\"\n"+
|
||||
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\",\"test1\",\"test2\",\"test3\""), 0, converter.DefaultImporterOptions.WithMemberAsTag().WithProjectAsTag().WithMerchantAsTag(), nil, nil, nil, nil, nil)
|
||||
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\",\"test1\",\"test2\",\"test3\""), time.UTC, converter.DefaultImporterOptions.WithMemberAsTag().WithProjectAsTag().WithMerchantAsTag(), nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -366,18 +366,18 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_InvalidRelatedId(t *testi
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrRelatedIdCannotBeBlank.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"转入\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrRelatedIdCannotBeBlank.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||
"\"转入\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"转入\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrFoundRecordNotHasRelatedRecord.Message)
|
||||
}
|
||||
|
||||
@@ -390,10 +390,10 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_MissingFileHeader(t *test
|
||||
DefaultCurrency: "CNY",
|
||||
}
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
}
|
||||
|
||||
@@ -409,36 +409,36 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_MissingRequiredColumn(t *
|
||||
// Missing Time Column
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Type Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"2024-09-01 00:00:00\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"2024-09-01 00:00:00\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Sub Category Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Account Name Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"123.45\",\"\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Amount Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"备注\",\"关联Id\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Related ID Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\"\n"+
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
package feidee
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
||||
@@ -34,7 +36,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the feidee mymoney (elecloud) transaction xlsx data
|
||||
func (c *feideeMymoneyElecloudTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *feideeMymoneyElecloudTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
dataTable, err := excel.CreateNewExcelOOXMLFileBasicDataTable(data, true)
|
||||
|
||||
if err != nil {
|
||||
@@ -45,5 +47,5 @@ func (c *feideeMymoneyElecloudTransactionDataXlsxFileImporter) ParseImportedData
|
||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, feideeMymoneyElecloudDataColumnNameMapping, transactionRowParser)
|
||||
dataTableImporter := converter.CreateNewSimpleImporter(feideeMymoneyElecloudTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ func TestFeideeMymoneyElecloudTransactionDataXlsxImporterParseImportedData_Minim
|
||||
testdata, err := os.ReadFile("../../../testdata/feidee_mymoney_elecloud_test_file.xlsx")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, testdata, 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, testdata, time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 7, len(allNewTransactions))
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package feidee
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
||||
@@ -33,7 +35,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the feidee mymoney (web) transaction xls data
|
||||
func (c *feideeMymoneyWebTransactionDataXlsFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *feideeMymoneyWebTransactionDataXlsFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
dataTable, err := excel.CreateNewExcelMSCFBFileBasicDataTable(data, true)
|
||||
|
||||
if err != nil {
|
||||
@@ -44,5 +46,5 @@ func (c *feideeMymoneyWebTransactionDataXlsFileImporter) ParseImportedData(ctx c
|
||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, feideeMymoneyWebDataColumnNameMapping, transactionRowParser)
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestFeideeMymoneyTransactionDataXlsImporterParseImportedData_MinimumValidDa
|
||||
testdata, err := os.ReadFile("../../../testdata/feidee_mymoney_test_file.xls")
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, testdata, 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, testdata, time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 7, len(allNewTransactions))
|
||||
|
||||
@@ -2,6 +2,7 @@ package fireflyIII
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/csv"
|
||||
@@ -40,7 +41,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the firefly III transaction csv data
|
||||
func (c *fireflyIIITransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *fireflyIIITransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
reader := bytes.NewReader(data)
|
||||
dataTable, err := csv.CreateNewCsvBasicDataTable(ctx, reader, true)
|
||||
|
||||
@@ -52,5 +53,5 @@ func (c *fireflyIIITransactionDataCsvFileImporter) ParseImportedData(ctx core.Co
|
||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, fireflyIIITransactionDataColumnNameMapping, transactionRowParser)
|
||||
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(fireflyIIITransactionTypeNameMapping, "", "", ",")
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package fireflyIII
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -25,7 +26,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_MinimumValidData(t *testing.
|
||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||
"Deposit,0.12,2024-09-01T01:23:45+08:00,\"A revenue account\",\"Test Account\",\"Test Category\"\n"+
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category2\"\n"+
|
||||
"Transfer,0.05,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category3\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,0.05,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -93,11 +94,11 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidTime(t *testing.
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56,\"Test Account\",\"A expense account\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-1.00,2024-09-01 12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-1.00,2024-09-01 12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidType(t *testing.
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Type,123.45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Type,123.45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -125,14 +126,14 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseAccountNameAsCategoryNa
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, "A expense account", allNewTransactions[0].OriginalCategoryName)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Deposit,10.00,2024-09-01T12:34:56+08:00,\"A revenue account\",\"Test Account\",\"\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Deposit,10.00,2024-09-01T12:34:56+08:00,\"A revenue account\",\"Test Account\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -149,19 +150,19 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidTimezone(t *testin
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56-10:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56-10:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56+00:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56+00:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56+12:45,\"Test Account\",\"A expense account\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-1.00,2024-09-01T12:34:56+12:45,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -178,7 +179,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidAccountCurrency(t
|
||||
|
||||
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category2\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category2\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -204,7 +205,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidForeignAmountAndCu
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"Transfer,10.00,15.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,10.00,15.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -215,7 +216,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidForeignAmountAndCu
|
||||
assert.Equal(t, "EUR", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -225,7 +226,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidForeignAmountAndCu
|
||||
assert.Equal(t, "EUR", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,,\"Test Account\",\"Test Account2\",\"Test Category\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,,\"Test Account\",\"Test Account2\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -244,12 +245,12 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidAccountCurrency(
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account\",\"Test Account2\",\"Test Category3\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account\",\"Test Account2\",\"Test Category3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account2\",\"Test Account\",\"Test Category3\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account2\",\"Test Account\",\"Test Category3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -263,11 +264,11 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseNotSupportedCurrency(t
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,XXX,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,XXX,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||
"Transfer,123.45,123.45,2024-09-01T23:59:59+08:00,USD,XXX,\"Test Account\",\"Test Account2\",\"Test Category2\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,123.45,123.45,2024-09-01T23:59:59+08:00,USD,XXX,\"Test Account\",\"Test Account2\",\"Test Category2\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -281,11 +282,11 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidAmount(t *testin
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-123 45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-123 45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,source_name,destination_name,category\n"+
|
||||
"Transfer,123.45,123 45,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category2\""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Transfer,123.45,123 45,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category2\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -299,7 +300,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseDescription(t *testing.
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,description,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-123.45,\"foo bar\t#test\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-123.45,\"foo bar\t#test\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -316,7 +317,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_ParseTags(t *testing.T) {
|
||||
}
|
||||
|
||||
allNewTransactions, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte("type,amount,tags,date,source_name,destination_name,category\n"+
|
||||
"Withdrawal,-123.45,\"tag1,tag2,tag3\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"Withdrawal,-123.45,\"tag1,tag2,tag3\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -338,7 +339,7 @@ func TestFireFlyIIICsvFileimporterParseImportedData_MissingFileHeader(t *testing
|
||||
DefaultCurrency: "CNY",
|
||||
}
|
||||
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -353,31 +354,31 @@ func TestFireFlyIIICsvFileimporterParseImportedData_MissingRequiredColumn(t *tes
|
||||
|
||||
// Missing Time Column
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,source_name,destination_name,category\n"+
|
||||
"\"Opening balance\",123.45,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"Opening balance\",123.45,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Type Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("amount,date,source_name,destination_name,category\n"+
|
||||
"123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Sub Category Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name\n"+
|
||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\"\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Account Name Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,destination_name,category\n"+
|
||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Test Account\",\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Amount Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,date,source_name,destination_name,category\n"+
|
||||
"\"Opening balance\",2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"Opening balance\",2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Account2 Name Column
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,category\n"+
|
||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (p *fireflyIIITransactionDataRowParser) Parse(data map[datatable.Transactio
|
||||
}
|
||||
|
||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||
}
|
||||
|
||||
// trim trailing zero in decimal
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package gnucash
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -24,7 +26,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the gnucash transaction data
|
||||
func (c *gnucashTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *gnucashTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
gnucashDataReader, err := createNewGnuCashDatabaseReader(data)
|
||||
|
||||
if err != nil {
|
||||
@@ -45,5 +47,5 @@ func (c *gnucashTransactionDataImporter) ParseImportedData(ctx core.Context, use
|
||||
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(gnucashTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -194,7 +195,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidData(t *tes
|
||||
DefaultCurrency: "CNY",
|
||||
}
|
||||
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(gnucashMinimumValidDataCase), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(gnucashMinimumValidDataCase), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
||||
@@ -218,7 +219,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_GzippedMinimumValidData
|
||||
assert.Nil(t, err)
|
||||
|
||||
gzippedData := buffer.Bytes()
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, gzippedData, 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, gzippedData, time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
||||
@@ -357,7 +358,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidDataWithRev
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
"</gnc:book>\n"+
|
||||
"</gnc-v2>\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</gnc-v2>\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
||||
@@ -389,7 +390,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidTime(t *tes
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -409,7 +410,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidTime(t *tes
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -439,7 +440,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidTimezone(t *t
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -461,7 +462,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidTimezone(t *t
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||
@@ -558,7 +559,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAccountCurren
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
"</gnc:book>\n"+
|
||||
"</gnc-v2>\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</gnc-v2>\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -600,7 +601,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *tes
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -623,7 +624,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *tes
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -656,7 +657,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *t
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -676,7 +677,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *t
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -696,7 +697,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *t
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -727,7 +728,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseDescription(t *tes
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -756,7 +757,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_SkipZeroAmountTransacti
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -806,7 +807,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_NotSupportedToParseSpli
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||
}
|
||||
|
||||
@@ -873,7 +874,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingAccountRequiredN
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -901,7 +902,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||
|
||||
// Missing Transaction Splits Node
|
||||
@@ -912,7 +913,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
||||
" <ts:date>2024-09-01 00:00:00 +0000</ts:date>\n"+
|
||||
" </trn:date-posted>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidGnuCashFile.Message)
|
||||
|
||||
// Missing Transaction Split Quantity Node
|
||||
@@ -931,7 +932,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
// Missing Transaction Split Account Node
|
||||
@@ -951,7 +952,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
||||
" </trn:split>\n"+
|
||||
" </trn:splits>\n"+
|
||||
"</gnc:transaction>\n"+
|
||||
gnucashCommonValidDataCaseFooter), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ func (t *gnucashTransactionDataRowIterator) parseTransaction(ctx core.Context, u
|
||||
}
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||
|
||||
if len(gnucashTransaction.Splits) == 2 {
|
||||
splitData1 := gnucashTransaction.Splits[0]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package iif
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -23,7 +25,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the intuit interchange format (iif) data
|
||||
func (c *iifTransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *iifTransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
iifDataReader := createNewIifDataReader(data)
|
||||
accountDatasets, transactionDatasets, err := iifDataReader.read(ctx)
|
||||
|
||||
@@ -39,5 +41,5 @@ func (c *iifTransactionDataFileImporter) ParseImportedData(ctx core.Context, use
|
||||
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(iifTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package iif
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -50,7 +51,7 @@ func TestIIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
||||
"ENDTRNS\t\t\t\t\n"+
|
||||
"TRNS\tCREDIT CARD\t09/07/2024\tTest Category2\t34.56\n"+
|
||||
"SPL\tCREDIT CARD\t09/07/2024\tTest Account2\t-34.56\n"+
|
||||
"ENDTRNS\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -147,7 +148,7 @@ func TestIIFTransactionDataFileParseImportedData_MinimumValidDataWithoutAccountD
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Category\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -203,7 +204,7 @@ func TestIIFTransactionDataFileParseImportedData_MultipleDataset(t *testing.T) {
|
||||
"ENDTRNS\t\t\t\t\n"+
|
||||
"!ACCNT\tTEST\tNAME\tACCNTTYPE\n"+
|
||||
"ACCNT\t\tTest Category\tINC\n"+
|
||||
"ACCNT\t\tTest Category2\tEXP\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ACCNT\t\tTest Category2\tEXP\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -268,7 +269,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseCategoryAndSubCategory(t *
|
||||
"ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/02/2024\tTest Account2\t-123.45\n"+
|
||||
"SPL\t09/02/2024\tTest Parent Category2:Test Category2\t123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -323,7 +324,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseYearMonthDayFormatTime(t *
|
||||
"ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t2024/9/4\tTest Account\t123.45\n"+
|
||||
"SPL\t2024/9/4\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -355,7 +356,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayYearFormatTim
|
||||
"ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t9/3/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t9/3/2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -386,7 +387,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayTwoDigitsYear
|
||||
"ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t24/9/3\tTest Account\t123.45\n"+
|
||||
"SPL\t24/9/3\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -411,7 +412,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T)
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09-01-2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09-01-2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -420,7 +421,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T)
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t2024-09-01\tTest Account\t123.45\n"+
|
||||
"SPL\t2024-09-01\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -429,7 +430,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T)
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t9/24\tTest Account\t123.45\n"+
|
||||
"SPL\t9/24\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -448,7 +449,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparat
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t9/01/2024\tTest Account\t123,456.78\n"+
|
||||
"SPL\t9/01/2024\tTest Account2\t-123,456.78\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -471,7 +472,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t123 45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -480,7 +481,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123 45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -499,7 +500,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
||||
"!ENDTRNS\t\t\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t\"Test\"\t123.45\t\"foo bar\t#test\"\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t\t-123.45\t\n"+
|
||||
"ENDTRNS\t\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -511,7 +512,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
||||
"!ENDTRNS\t\t\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\tTest\t123.45\t\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t\t-123.45\t\n"+
|
||||
"ENDTRNS\t\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -553,7 +554,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransaction(t *testin
|
||||
"TRNS\t09/05/2024\tTest Category2\t100.00\n"+
|
||||
"SPL\t09/05/2024\tTest Account3\t-40.00\n"+
|
||||
"SPL\t09/05/2024\tTest Account4\t-60.00\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -667,7 +668,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransactionDescriptio
|
||||
"TRNS\t09/01/2024\tTest Account\t\"Test\"\t123.45\t\"foo bar\t#test\"\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t\t-100.00\t\"foo\ttest#bar\"\n"+
|
||||
"SPL\t09/01/2024\tTest Account3\t\t-23.45\t\n"+
|
||||
"ENDTRNS\t\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(allNewTransactions))
|
||||
@@ -682,7 +683,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransactionDescriptio
|
||||
"SPL\t09/01/2024\tTest Account2\t\t-100.00\t\"test\"\n"+
|
||||
"SPL\t09/01/2024\tTest Account3\tfoo\t-12.34\t\n"+
|
||||
"SPL\t09/01/2024\tTest Account4\t\t-11.11\t\n"+
|
||||
"ENDTRNS\t\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(allNewTransactions))
|
||||
@@ -708,7 +709,7 @@ func TestIIFTransactionDataFileParseImportedData_NotSupportedSplitTransaction(t
|
||||
"TRNS\tBEGINBALCHECK\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\tBEGINBALCHECK\t09/01/2024\tTest Account2\t-100.00\n"+
|
||||
"SPL\tBEGINBALCHECK\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||
|
||||
// Transaction with invalid amount
|
||||
@@ -719,7 +720,7 @@ func TestIIFTransactionDataFileParseImportedData_NotSupportedSplitTransaction(t
|
||||
"TRNS\t09/01/2024\tTest Account\t123 45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-100.00\n"+
|
||||
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
// Transaction split data with invalid amount
|
||||
@@ -730,7 +731,7 @@ func TestIIFTransactionDataFileParseImportedData_NotSupportedSplitTransaction(t
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-100 00\n"+
|
||||
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
|
||||
// Transaction amount not equal to sum of split data amount
|
||||
@@ -741,7 +742,7 @@ func TestIIFTransactionDataFileParseImportedData_NotSupportedSplitTransaction(t
|
||||
"TRNS\t09/01/2024\tTest Account\t123.00\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-100.00\n"+
|
||||
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||
}
|
||||
|
||||
@@ -760,7 +761,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Transaction And Split Line
|
||||
@@ -768,7 +769,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Split Line
|
||||
@@ -777,7 +778,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Transaction End Line
|
||||
@@ -786,7 +787,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Transaction End Line (following is another header)
|
||||
@@ -797,7 +798,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
||||
"ACCNT\tTest Account\tBANK\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ACCNT\tTest Account\tBANK\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Invalid Line
|
||||
@@ -808,7 +809,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"TEST\t\t\t\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Repeat Transaction Line
|
||||
@@ -819,7 +820,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Repeat Transaction End Line
|
||||
@@ -831,7 +832,7 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
}
|
||||
|
||||
@@ -848,25 +849,25 @@ func TestIIFTransactionDataFileParseImportedData_InvalidHeaderLines(t *testing.T
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Transaction Sample Line
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"!ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Split Sample Line
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"!ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Transaction End Sample Line
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Missing Transaction End Sample Line (following is data line)
|
||||
@@ -875,14 +876,14 @@ func TestIIFTransactionDataFileParseImportedData_InvalidHeaderLines(t *testing.T
|
||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
|
||||
// Invalid Sample Line
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!TEST\tDATE\tACCNT\tAMOUNT\n"+
|
||||
"!ENDTRNS\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"!ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||
}
|
||||
|
||||
@@ -902,7 +903,7 @@ func TestIIFTransactionDataFileParseImportedData_MissingRequiredColumn(t *testin
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\tTest Account\t123.45\n"+
|
||||
"SPL\tTest Account2\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Account Column
|
||||
@@ -912,7 +913,7 @@ func TestIIFTransactionDataFileParseImportedData_MissingRequiredColumn(t *testin
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\t123.45\n"+
|
||||
"SPL\t09/01/2024\t-123.45\n"+
|
||||
"ENDTRNS\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Amount Column
|
||||
@@ -922,6 +923,6 @@ func TestIIFTransactionDataFileParseImportedData_MissingRequiredColumn(t *testin
|
||||
"!ENDTRNS\t\t\t\n"+
|
||||
"TRNS\t09/01/2024\tTest Account\n"+
|
||||
"SPL\t09/01/2024\tTest Account2\n"+
|
||||
"ENDTRNS\t\t\t\t\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package jdcom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
@@ -27,7 +28,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the jd.com finance transaction csv data
|
||||
func (c *jdComFinanceTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *jdComFinanceTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
fallback := unicode.UTF8.NewDecoder()
|
||||
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
||||
|
||||
@@ -60,5 +61,5 @@ func (c *jdComFinanceTransactionDataCsvFileImporter) ParseImportedData(ctx core.
|
||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, jdComFinanceTransactionSupportedColumns, transactionRowParser)
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(jdComFinanceTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MinimumValidData(t *testin
|
||||
"2025-09-01 12:34:56,xxx,xxx,123.45,银行卡,交易成功,支出,其他网购\n" +
|
||||
"2025-09-01 23:59:59,xxx,京东钱包余额充值,0.05,银行卡,交易成功,不计收支,余额\n" +
|
||||
"2025-09-02 23:59:59,xxx,京东余额提现,0.03,银行卡,交易成功,不计收支,余额\n"
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 4, len(allNewTransactions))
|
||||
@@ -111,7 +111,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseRefundTransaction(t *
|
||||
"2025-09-01 02:34:56,xxx,xxx,0.12(已全额退款),银行卡,交易成功,不计收支\n" +
|
||||
"2025-09-02 01:23:45,xxx,xxx,3.45,银行卡,退款成功,不计收支\n" +
|
||||
"2025-09-02 02:34:56,xxx,xxx,123.45(已退款3.45),银行卡,交易成功,支出\n"
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||
@@ -154,7 +154,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidTime(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01T01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
data2 := "导出信息:\n" +
|
||||
@@ -163,7 +163,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidTime(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"09/01/2025 01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidType(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,转账\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidAmount(t *test
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,¥0.12,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支,交易分类\n" +
|
||||
"2025-09-01 01:23:45,xxx,京东钱包余额充值,0.05,银行卡,交易成功,不计收支,余额\n"
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -236,7 +236,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支,交易分类\n" +
|
||||
"2025-09-01 01:23:45,xxx,京东余额提现,0.05,银行卡,交易成功,不计收支,余额\n"
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -253,7 +253,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
||||
"2025-09-01 01:23:45,xxx,京东小金库-转入,0.05,余额,交易成功,不计收支,小金库\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -270,7 +270,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
||||
"2025-09-01 01:23:45,xxx,京东小金库-转出,0.05,余额,交易成功,不计收支,小金库\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -287,7 +287,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
||||
"2025-09-01 01:23:45,xxx,价保退款,0.05,银行卡,交易成功,不计收支,其他\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -303,7 +303,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
||||
"2025-09-01 01:23:45,xxx,白条主动还款,0.05,银行卡,交易成功,不计收支,白条\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -327,7 +327,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,,0.12,银行卡,交易成功,支出\n"
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -339,7 +339,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,交易说明,金额,收/付款方式,交易状态,收/支,备注\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,Test,0.12,银行卡,交易成功,支出,\"foo\"\"bar,\ntest\"\n"
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, "foo\"bar,\ntest", allNewTransactions[0].Comment)
|
||||
|
||||
@@ -349,7 +349,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testin
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,交易说明,金额,收/付款方式,交易状态,收/支,备注\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,Test,0.12,银行卡,交易成功,支出,\n"
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
||||
}
|
||||
@@ -369,7 +369,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownStatusTransacti
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,xxxx,支出\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownMemoTransferTra
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,不计收支\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -403,10 +403,10 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingFileHeader(t *testi
|
||||
|
||||
data := "交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
||||
"\n" +
|
||||
"商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
|
||||
// Missing Merchant Name Column
|
||||
@@ -436,7 +436,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
||||
"\n" +
|
||||
"交易时间,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,0.12,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Transaction Memo Column
|
||||
@@ -446,7 +446,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
||||
"\n" +
|
||||
"交易时间,商户名称,金额,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,0.12,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Amount Column
|
||||
@@ -456,7 +456,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,收/付款方式,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,银行卡,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Related Account Column
|
||||
@@ -466,7 +466,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,交易状态,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,0.12,交易成功,支出\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Status Column
|
||||
@@ -476,7 +476,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,收/支\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,支出\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Type Column
|
||||
@@ -486,7 +486,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态\n" +
|
||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data7), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data7), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
}
|
||||
|
||||
@@ -504,6 +504,6 @@ func TestJDComFinanceCsvFileImporterParseImportedData_NoTransactionData(t *testi
|
||||
"日期区间:2025-01-01 至 2025-09-01\n" +
|
||||
"\n" +
|
||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package mt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -22,7 +24,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the mt940 file statement data
|
||||
func (c *mt940TransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *mt940TransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
mt940DataReader := createNewMT940FileReader(data)
|
||||
mt940Data, err := mt940DataReader.read(ctx)
|
||||
|
||||
@@ -38,5 +40,5 @@ func (c *mt940TransactionDataFileImporter) ParseImportedData(ctx core.Context, u
|
||||
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(mt940TransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package mt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -32,7 +33,7 @@ func TestMT940TransactionDataFileParseImportedData_MinimumValidData(t *testing.T
|
||||
:61:2506020603D234,56NTRFFOOBAR
|
||||
:86:Transaction 2
|
||||
:62F:C250601CNY123,45
|
||||
-}`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -92,7 +93,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionValidAmountAn
|
||||
:61:250603C1,NTRFTEST
|
||||
:86:Transaction 3
|
||||
:62F:C250601CNY123,45
|
||||
-}`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(allNewTransactions))
|
||||
@@ -118,7 +119,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionInvalidAmount
|
||||
:60F:C250601CNY123,45
|
||||
:61:2506010602C123 45NTRFTEST
|
||||
:62F:C250601CNY123,45
|
||||
-}`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidMT940File.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -129,7 +130,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionInvalidAmount
|
||||
:60F:C250601CNY123,45
|
||||
:61:2506010602C12.45NTRFTEST
|
||||
:62F:C250601CNY123,45
|
||||
-}`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidMT940File.Message)
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionType(t *testi
|
||||
:61:250604RD123,45NTRFTEST
|
||||
:86:Transaction 4
|
||||
:62F:C250601CNY123,45
|
||||
-}`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 4, len(allNewTransactions))
|
||||
@@ -187,7 +188,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseDescription(t *testing.T
|
||||
Part 2
|
||||
Part 3
|
||||
:62F:C250601CNY123,45
|
||||
-}`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -210,6 +211,6 @@ func TestMT940TransactionDataFileParseImportedData_MissingRequiredField(t *testi
|
||||
:28C:123/1
|
||||
:61:250601C123,45NTRFTEST
|
||||
:86:Transaction 1
|
||||
-}`), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package ofx
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -23,7 +25,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the open financial exchange (ofx) file transaction data
|
||||
func (c *ofxTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *ofxTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
ofxDataReader, err := createNewOFXFileReader(ctx, data)
|
||||
|
||||
if err != nil {
|
||||
@@ -44,5 +46,5 @@ func (c *ofxTransactionDataImporter) ParseImportedData(ctx core.Context, user *m
|
||||
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(ofxTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ofx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -77,7 +78,7 @@ func TestOFXTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
||||
" </CCSTMTRS>\n"+
|
||||
" </CCSTMTTRNRS>\n"+
|
||||
" </CREDITCARDMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -211,7 +212,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAccountTo(t *testing.T) {
|
||||
" </CCSTMTRS>\n"+
|
||||
" </CCSTMTTRNRS>\n"+
|
||||
" </CREDITCARDMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -296,7 +297,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseValidTransactionTime(t *te
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -338,7 +339,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -360,7 +361,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -382,7 +383,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
@@ -404,7 +405,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -436,7 +437,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAmount_CommaAsDecimalPoint
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -472,7 +473,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -505,7 +506,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseTransactionCurrency(t *tes
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -542,7 +543,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -568,7 +569,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -596,7 +597,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -629,7 +630,7 @@ func TestOFXTransactionDataFileParseImportedData_MissingAccountFromNode(t *testi
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
||||
}
|
||||
|
||||
@@ -661,7 +662,7 @@ func TestOFXTransactionDataFileParseImportedData_MissingCurrencyNode(t *testing.
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -693,7 +694,7 @@ func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||
|
||||
// Missing Transaction Type Node
|
||||
@@ -715,7 +716,7 @@ func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||
|
||||
// Missing Amount Node
|
||||
@@ -737,6 +738,6 @@ func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(
|
||||
" </STMTRS>\n"+
|
||||
" </STMTTRNRS>\n"+
|
||||
" </BANKMSGSRSV1>\n"+
|
||||
"</OFX>"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package qif
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -35,7 +37,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the quicken interchange format (qif) transaction data
|
||||
func (c *qifTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *qifTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
qifDataReader := createNewQifDataReader(data)
|
||||
qifData, err := qifDataReader.read(ctx)
|
||||
|
||||
@@ -51,5 +53,5 @@ func (c *qifTransactionDataImporter) ParseImportedData(ctx core.Context, user *m
|
||||
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(qifTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package qif
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -43,7 +44,7 @@ func TestQIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
||||
"D2024-09-05\n"+
|
||||
"T0.06\n"+
|
||||
"L[Test Account2]\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -138,7 +139,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseYearMonthDayDateFormatTime
|
||||
"^\n"+
|
||||
"D2024'9.5\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -176,7 +177,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseMonthDayYearDateFormatTime
|
||||
"^\n"+
|
||||
"D9.5'2024\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -214,7 +215,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseDayYearMonthDateFormatTime
|
||||
"^\n"+
|
||||
"D5'9.2024\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -252,7 +253,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseShortYearMonthDayDateForma
|
||||
"^\n"+
|
||||
"D24'9.5\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -278,7 +279,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T)
|
||||
"!Type:Bank\n"+
|
||||
"D2024 09 01\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -295,7 +296,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparat
|
||||
"!Type:Bank\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T-123,456.78\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -317,7 +318,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
||||
"!Type:Bank\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T-123 45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -334,7 +335,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
||||
"!Type:Cash\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -347,7 +348,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
||||
"!Type:CCard\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -360,7 +361,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
||||
"!Type:Oth A\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -373,7 +374,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
||||
"!Type:Oth L\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -399,7 +400,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccount(t *testing.T) {
|
||||
"!Type:Bank\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -425,7 +426,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAmountWithLeadingPlusSign(
|
||||
"!Type:Bank\n"+
|
||||
"D2024-09-01\n"+
|
||||
"T+123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -446,7 +447,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseSubCategory(t *testing.T)
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"LTest Category:Sub Category\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -477,7 +478,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
||||
"D2024-09-02\n"+
|
||||
"T-234.56\n"+
|
||||
"PTest2\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(allNewTransactions))
|
||||
@@ -494,7 +495,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
||||
"D2024-09-02\n"+
|
||||
"T-234.56\n"+
|
||||
"PTest2\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions.WithPayeeAsDescription(), nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions.WithPayeeAsDescription(), nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(allNewTransactions))
|
||||
@@ -516,7 +517,7 @@ func TestQIFTransactionDataFileParseImportedData_WithAdditionalOptions(t *testin
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"PTest2\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -527,7 +528,7 @@ func TestQIFTransactionDataFileParseImportedData_WithAdditionalOptions(t *testin
|
||||
"D2024-09-01\n"+
|
||||
"T-123.45\n"+
|
||||
"PTest2\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions.WithPayeeAsTag(), nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions.WithPayeeAsTag(), nil, nil, nil, nil, nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -548,13 +549,13 @@ func TestQIFTransactionDataFileParseImportedData_MissingRequiredFields(t *testin
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||
"!Type:Bank\n"+
|
||||
"T-123.45\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||
|
||||
// Missing Amount Field
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||
"!Type:Bank\n"+
|
||||
"D2024-09-01\n"+
|
||||
"^\n"), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package wechat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
@@ -27,7 +28,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the wechat pay transaction csv data
|
||||
func (c *wechatPayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *wechatPayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
fallback := unicode.UTF8.NewDecoder()
|
||||
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
||||
|
||||
@@ -58,5 +59,5 @@ func (c *wechatPayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Con
|
||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, wechatPayTransactionSupportedColumns, transactionRowParser)
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(wechatPayTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T
|
||||
"2024-09-01 12:34:56,商户消费,支出,¥123.45,支付成功\n" +
|
||||
"2024-09-01 23:59:59,零钱充值,/,¥0.05,充值完成\n" +
|
||||
"2024-09-02 23:59:59,零钱提现,/,¥0.03,提现已到账\n"
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 4, len(allNewTransactions))
|
||||
@@ -109,7 +109,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseRefundTransaction(t *tes
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||
"2024-09-01 01:23:45,xxx-退款,收入,¥0.12,已全额退款\n"
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||
@@ -136,7 +136,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||
"2024-09-01T01:23:45,二维码收款,收入,¥0.12,已收钱\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
|
||||
data2 := "微信支付账单明细,,,,\n" +
|
||||
@@ -146,7 +146,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||
"09/01/2024 12:34:56,二维码收款,收入,¥0.12,已收钱\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||
"2024-09-01T01:23:45,xxx,,¥0.12,支付成功\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,收入,¥,已收钱\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),支付方式,当前状态\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,/,已收钱\n"
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -223,7 +223,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
||||
"2024-09-01 01:23:45,xxx-退款,收入,¥0.12,test,已全额退款\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -239,7 +239,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
||||
"2024-09-01 23:59:59,零钱充值,/,¥0.05,test,充值完成\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -256,7 +256,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
||||
"2024-09-02 23:59:59,零钱提现,/,¥0.03,test,提现已到账\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -273,7 +273,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
||||
"2024-09-03 23:59:59,信用卡还款,/,¥0.01,零钱,支付成功\n"
|
||||
assert.Nil(t, err)
|
||||
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -297,7 +297,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态,备注\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,已收钱,\"/\"\n"
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
@@ -310,7 +310,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,商品,收/支,金额(元),当前状态,备注\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,Test,收入,¥0.12,已收钱,\"foo\"\"bar,\ntest\"\n"
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, "foo\"bar,\ntest", allNewTransactions[0].Comment)
|
||||
|
||||
@@ -321,7 +321,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,商品,收/支,金额(元),当前状态,备注\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,Test,收入,¥0.12,已收钱,\"\"\n"
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.Equal(t, 1, len(allNewTransactions))
|
||||
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
||||
}
|
||||
@@ -342,7 +342,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_SkipUnknownTransferTransactio
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||
"2024-09-01 23:59:59,/,/,¥0.05,充值完成\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -357,10 +357,10 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.
|
||||
|
||||
data := "交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,已收钱\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易类型,收/支,金额(元),当前状态\n" +
|
||||
"二维码收款,收入,¥0.12,已收钱\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Category Column
|
||||
@@ -392,7 +392,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,收/支,金额(元),当前状态\n" +
|
||||
"2024-09-01 01:23:45,收入,¥0.12,已收钱\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Type Column
|
||||
@@ -403,7 +403,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,金额(元),当前状态\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,¥0.12,已收钱\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Amount Column
|
||||
@@ -414,7 +414,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,当前状态\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,收入,已收钱\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
|
||||
// Missing Status Column
|
||||
@@ -425,7 +425,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元)\n" +
|
||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12\n"
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||
}
|
||||
|
||||
@@ -444,6 +444,6 @@ func TestWeChatPayCsvFileImporterParseImportedData_NoTransactionData(t *testing.
|
||||
",,,,\n" +
|
||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||
"交易时间,交易类型,收/支,金额(元),当前状态\n"
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), 0, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
||||
@@ -21,7 +23,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseImportedData returns the imported data by parsing the wechat pay transaction csv data
|
||||
func (c *wechatPayTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
func (c *wechatPayTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||
xlsxDataTable, err := excel.CreateNewExcelOOXMLFileBasicDataTable(data, false)
|
||||
|
||||
if err != nil {
|
||||
@@ -49,5 +51,5 @@ func (c *wechatPayTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Co
|
||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, wechatPayTransactionSupportedColumns, transactionRowParser)
|
||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(wechatPayTransactionTypeNameMapping)
|
||||
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ func (h *mcpAddTransactionToolHandler) createNewTransactionModel(uid int64, addT
|
||||
Type: transactionDbType,
|
||||
CategoryId: categoryId,
|
||||
TransactionTime: utils.GetMinTransactionTimeFromUnixTime(transactionTime.Unix()),
|
||||
TimezoneUtcOffset: utils.GetTimezoneOffsetMinutes(transactionTime.Location()),
|
||||
TimezoneUtcOffset: utils.GetTimezoneOffsetMinutes(transactionTime.Unix(), transactionTime.Location()),
|
||||
AccountId: sourceAccountId,
|
||||
Amount: amount,
|
||||
HideAmount: false,
|
||||
|
||||
@@ -219,7 +219,7 @@ func (s *AccountService) GetMaxSubAccountDisplayOrder(c core.Context, uid int64,
|
||||
}
|
||||
|
||||
// CreateAccounts saves a new account model to database
|
||||
func (s *AccountService) CreateAccounts(c core.Context, mainAccount *models.Account, mainAccountBalanceTime int64, childrenAccounts []*models.Account, childrenAccountBalanceTimes []int64, utcOffset int16) error {
|
||||
func (s *AccountService) CreateAccounts(c core.Context, mainAccount *models.Account, mainAccountBalanceTime int64, childrenAccounts []*models.Account, childrenAccountBalanceTimes []int64, clientTimezone *time.Location) error {
|
||||
if mainAccount.Uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
@@ -266,11 +266,14 @@ func (s *AccountService) CreateAccounts(c core.Context, mainAccount *models.Acco
|
||||
}
|
||||
|
||||
transactionTime := defaultTransactionTime
|
||||
transactionUtcOffset := utils.GetTimezoneOffsetMinutes(now, clientTimezone)
|
||||
|
||||
if i == 0 && mainAccountBalanceTime > 0 {
|
||||
transactionTime = utils.GetMinTransactionTimeFromUnixTime(mainAccountBalanceTime)
|
||||
transactionUtcOffset = utils.GetTimezoneOffsetMinutes(mainAccountBalanceTime, clientTimezone)
|
||||
} else if i > 0 && len(childrenAccountBalanceTimes) > i-1 && childrenAccountBalanceTimes[i-1] > 0 {
|
||||
transactionTime = utils.GetMinTransactionTimeFromUnixTime(childrenAccountBalanceTimes[i-1])
|
||||
transactionUtcOffset = utils.GetTimezoneOffsetMinutes(childrenAccountBalanceTimes[i-1], clientTimezone)
|
||||
} else {
|
||||
defaultTransactionTime++
|
||||
}
|
||||
@@ -281,7 +284,7 @@ func (s *AccountService) CreateAccounts(c core.Context, mainAccount *models.Acco
|
||||
Deleted: false,
|
||||
Type: models.TRANSACTION_DB_TYPE_MODIFY_BALANCE,
|
||||
TransactionTime: transactionTime,
|
||||
TimezoneUtcOffset: utcOffset,
|
||||
TimezoneUtcOffset: transactionUtcOffset,
|
||||
AccountId: allAccounts[i].AccountId,
|
||||
Amount: allAccounts[i].Balance,
|
||||
RelatedAccountId: allAccounts[i].AccountId,
|
||||
@@ -366,7 +369,7 @@ func (s *AccountService) CreateAccounts(c core.Context, mainAccount *models.Acco
|
||||
}
|
||||
|
||||
// ModifyAccounts saves an existed account model to database
|
||||
func (s *AccountService) ModifyAccounts(c core.Context, mainAccount *models.Account, updateAccounts []*models.Account, addSubAccounts []*models.Account, addSubAccountBalanceTimes []int64, removeSubAccountIds []int64, utcOffset int16) error {
|
||||
func (s *AccountService) ModifyAccounts(c core.Context, mainAccount *models.Account, updateAccounts []*models.Account, addSubAccounts []*models.Account, addSubAccountBalanceTimes []int64, removeSubAccountIds []int64, clientTimezone *time.Location) error {
|
||||
if mainAccount.Uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
@@ -407,9 +410,11 @@ func (s *AccountService) ModifyAccounts(c core.Context, mainAccount *models.Acco
|
||||
}
|
||||
|
||||
transactionTime := defaultTransactionTime
|
||||
transactionUtcOffset := utils.GetTimezoneOffsetMinutes(now, clientTimezone)
|
||||
|
||||
if len(addSubAccountBalanceTimes) > i && addSubAccountBalanceTimes[i] > 0 {
|
||||
transactionTime = utils.GetMinTransactionTimeFromUnixTime(addSubAccountBalanceTimes[i])
|
||||
transactionUtcOffset = utils.GetTimezoneOffsetMinutes(addSubAccountBalanceTimes[i], clientTimezone)
|
||||
} else {
|
||||
defaultTransactionTime++
|
||||
}
|
||||
@@ -420,7 +425,7 @@ func (s *AccountService) ModifyAccounts(c core.Context, mainAccount *models.Acco
|
||||
Deleted: false,
|
||||
Type: models.TRANSACTION_DB_TYPE_MODIFY_BALANCE,
|
||||
TransactionTime: transactionTime,
|
||||
TimezoneUtcOffset: utcOffset,
|
||||
TimezoneUtcOffset: transactionUtcOffset,
|
||||
AccountId: childAccount.AccountId,
|
||||
Amount: childAccount.Balance,
|
||||
RelatedAccountId: childAccount.AccountId,
|
||||
|
||||
+15
-10
@@ -224,12 +224,17 @@ func ParseFromLongDateTimeToMaxUnixTime(t string) (time.Time, error) {
|
||||
return time.ParseInLocation(longDateTimeFormat, t, timezone)
|
||||
}
|
||||
|
||||
// ParseFromLongDateTime parses a formatted string in long date time format
|
||||
func ParseFromLongDateTime(t string, utcOffset int16) (time.Time, error) {
|
||||
// ParseFromLongDateTimeInFixedUtcOffset parses a formatted string in long date time format
|
||||
func ParseFromLongDateTimeInFixedUtcOffset(t string, utcOffset int16) (time.Time, error) {
|
||||
timezone := time.FixedZone("Timezone", int(utcOffset)*60)
|
||||
return time.ParseInLocation(longDateTimeFormat, t, timezone)
|
||||
}
|
||||
|
||||
// ParseFromLongDateTimeInTimeZone parses a formatted string in long date time format
|
||||
func ParseFromLongDateTimeInTimeZone(t string, timezone *time.Location) (time.Time, error) {
|
||||
return time.ParseInLocation(longDateTimeFormat, t, timezone)
|
||||
}
|
||||
|
||||
// ParseFromLongDateTimeWithTimezone parses a formatted string in long date time format
|
||||
func ParseFromLongDateTimeWithTimezone(t string) (time.Time, error) {
|
||||
return time.Parse(longDateTimeWithTimezoneFormat, t)
|
||||
@@ -245,14 +250,14 @@ func ParseFromLongDateTimeWithTimezoneRFC3339Format(t string) (time.Time, error)
|
||||
return time.Parse(longDateTimeWithTimezoneRFC3339Format, t)
|
||||
}
|
||||
|
||||
// ParseFromLongDateTimeWithoutSecond parses a formatted string in long date time format (no second)
|
||||
func ParseFromLongDateTimeWithoutSecond(t string, utcOffset int16) (time.Time, error) {
|
||||
// ParseFromLongDateTimeWithoutSecondInFixedUtcOffset parses a formatted string in long date time format (no second) with fixed UTC offset
|
||||
func ParseFromLongDateTimeWithoutSecondInFixedUtcOffset(t string, utcOffset int16) (time.Time, error) {
|
||||
timezone := time.FixedZone("Timezone", int(utcOffset)*60)
|
||||
return time.ParseInLocation(longDateTimeWithoutSecondFormat, t, timezone)
|
||||
}
|
||||
|
||||
// ParseFromShortDateTime parses a formatted string in short date time format
|
||||
func ParseFromShortDateTime(t string, utcOffset int16) (time.Time, error) {
|
||||
// ParseFromShortDateTimeInFixedUtcOffset parses a formatted string in short date time format with fixed UTC offset
|
||||
func ParseFromShortDateTimeInFixedUtcOffset(t string, utcOffset int16) (time.Time, error) {
|
||||
timezone := time.FixedZone("Timezone", int(utcOffset)*60)
|
||||
return time.ParseInLocation(shortDateTimeFormat, t, timezone)
|
||||
}
|
||||
@@ -282,8 +287,8 @@ func IsUnixTimeEqualsYearAndMonth(unixTime int64, timezone *time.Location, year
|
||||
}
|
||||
|
||||
// GetTimezoneOffsetMinutes returns offset minutes according specified timezone
|
||||
func GetTimezoneOffsetMinutes(timezone *time.Location) int16 {
|
||||
_, tzOffset := time.Now().In(timezone).Zone()
|
||||
func GetTimezoneOffsetMinutes(unixTime int64, timezone *time.Location) int16 {
|
||||
_, tzOffset := parseFromUnixTime(unixTime).In(timezone).Zone()
|
||||
tzMinuteOffset := int16(tzOffset / 60)
|
||||
|
||||
return tzMinuteOffset
|
||||
@@ -298,8 +303,8 @@ func GetServerTimezoneOffsetMinutes() int16 {
|
||||
}
|
||||
|
||||
// FormatTimezoneOffset returns "+/-HH:MM" format of timezone
|
||||
func FormatTimezoneOffset(timezone *time.Location) string {
|
||||
tzMinutesOffset := GetTimezoneOffsetMinutes(timezone)
|
||||
func FormatTimezoneOffset(unixTime int64, timezone *time.Location) string {
|
||||
tzMinutesOffset := GetTimezoneOffsetMinutes(unixTime, timezone)
|
||||
|
||||
sign := "+"
|
||||
hourAbsOffset := tzMinutesOffset / 60
|
||||
|
||||
+68
-17
@@ -215,15 +215,36 @@ func TestParseFromLongDateTimeToMaxUnixTime(t *testing.T) {
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestParseFromLongDateTime(t *testing.T) {
|
||||
func TestParseFromLongDateTimeInFixedUtcOffset(t *testing.T) {
|
||||
expectedValue := int64(1617228083)
|
||||
actualTime, err := ParseFromLongDateTime("2021-04-01 06:01:23", 480)
|
||||
actualTime, err := ParseFromLongDateTimeInFixedUtcOffset("2021-04-01 06:01:23", 480)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
actualValue := actualTime.Unix()
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestParseFromLongDateTimeInTimeZone(t *testing.T) {
|
||||
londonLocation, err := time.LoadLocation("Europe/London")
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// during standard time (UTC+0)
|
||||
expectedValue := int64(1577858483)
|
||||
actualTime, err := ParseFromLongDateTimeInTimeZone("2020-01-01 06:01:23", londonLocation)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
actualValue := actualTime.Unix()
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
// during daylight saving time (UTC+1)
|
||||
expectedValue = int64(1619845283)
|
||||
actualTime, err = ParseFromLongDateTimeInTimeZone("2021-05-01 06:01:23", londonLocation)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
actualValue = actualTime.Unix()
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestParseFromLongDateTimeWithTimezone(t *testing.T) {
|
||||
expectedValue := int64(1617238883)
|
||||
actualTime, err := ParseFromLongDateTimeWithTimezone("2021-04-01 06:01:23+05:00")
|
||||
@@ -251,18 +272,18 @@ func TestParseFromLongDateTimeWithTimezoneRFC3339Format(t *testing.T) {
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestParseFromLongDateTimeWithoutSecond(t *testing.T) {
|
||||
func TestParseFromLongDateTimeWithoutSecondInFixedUtcOffset(t *testing.T) {
|
||||
expectedValue := int64(1691947440)
|
||||
actualTime, err := ParseFromLongDateTimeWithoutSecond("2023-08-13 17:24", 0)
|
||||
actualTime, err := ParseFromLongDateTimeWithoutSecondInFixedUtcOffset("2023-08-13 17:24", 0)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
actualValue := actualTime.Unix()
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestParseFromShortDateTime(t *testing.T) {
|
||||
func TestParseFromShortDateTimeInFixedUtcOffset(t *testing.T) {
|
||||
expectedValue := int64(1617228083)
|
||||
actualTime, err := ParseFromShortDateTime("2021-4-1 6:1:23", 480)
|
||||
actualTime, err := ParseFromShortDateTimeInFixedUtcOffset("2021-4-1 6:1:23", 480)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
actualValue := actualTime.Unix()
|
||||
@@ -312,52 +333,82 @@ func TestIsUnixTimeEqualsYearAndMonth(t *testing.T) {
|
||||
assert.Equal(t, false, actualValue)
|
||||
}
|
||||
|
||||
func TestGetTimezoneOffsetMinutes(t *testing.T) {
|
||||
func TestGetTimezoneOffsetMinutes_FixedTimezone(t *testing.T) {
|
||||
timezone := time.FixedZone("Test Timezone", 120*60)
|
||||
expectedValue := int16(120)
|
||||
actualValue := GetTimezoneOffsetMinutes(timezone)
|
||||
actualValue := GetTimezoneOffsetMinutes(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
timezone = time.FixedZone("Test Timezone", 345*60)
|
||||
expectedValue = int16(345)
|
||||
actualValue = GetTimezoneOffsetMinutes(timezone)
|
||||
actualValue = GetTimezoneOffsetMinutes(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
timezone = time.FixedZone("Test Timezone", -720*60)
|
||||
expectedValue = int16(-720)
|
||||
actualValue = GetTimezoneOffsetMinutes(timezone)
|
||||
actualValue = GetTimezoneOffsetMinutes(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
timezone = time.FixedZone("Test Timezone", 0)
|
||||
expectedValue = int16(0)
|
||||
actualValue = GetTimezoneOffsetMinutes(timezone)
|
||||
actualValue = GetTimezoneOffsetMinutes(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestFormatTimezoneOffset(t *testing.T) {
|
||||
func TestGetTimezoneOffsetMinutes_TimezoneWithDST(t *testing.T) {
|
||||
londonLocation, err := time.LoadLocation("Europe/London")
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// during standard time (UTC+0)
|
||||
expectedValue := int16(0)
|
||||
actualValue := GetTimezoneOffsetMinutes(1577858483, londonLocation) // 2020-01-01 06:01:23 +00:00
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
// during daylight saving time (UTC+1)
|
||||
expectedValue = int16(60)
|
||||
actualValue = GetTimezoneOffsetMinutes(1619845283, londonLocation) // 2021-05-01 06:01:23 +01:00
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestFormatTimezoneOffset_FixedTimezone(t *testing.T) {
|
||||
timezone := time.FixedZone("Test Timezone", 120*60)
|
||||
expectedValue := "+02:00"
|
||||
actualValue := FormatTimezoneOffset(timezone)
|
||||
actualValue := FormatTimezoneOffset(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
timezone = time.FixedZone("Test Timezone", 345*60)
|
||||
expectedValue = "+05:45"
|
||||
actualValue = FormatTimezoneOffset(timezone)
|
||||
actualValue = FormatTimezoneOffset(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
timezone = time.FixedZone("Test Timezone", -720*60)
|
||||
expectedValue = "-12:00"
|
||||
actualValue = FormatTimezoneOffset(timezone)
|
||||
actualValue = FormatTimezoneOffset(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
timezone = time.FixedZone("Test Timezone", -150*60)
|
||||
expectedValue = "-02:30"
|
||||
actualValue = FormatTimezoneOffset(timezone)
|
||||
actualValue = FormatTimezoneOffset(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
timezone = time.FixedZone("Test Timezone", 0)
|
||||
expectedValue = "+00:00"
|
||||
actualValue = FormatTimezoneOffset(timezone)
|
||||
actualValue = FormatTimezoneOffset(time.Now().Unix(), timezone)
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
func TestFormatTimezoneOffset_TimezoneWithDST(t *testing.T) {
|
||||
londonLocation, err := time.LoadLocation("Europe/London")
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// during standard time (UTC+0)
|
||||
expectedValue := "+00:00"
|
||||
actualValue := FormatTimezoneOffset(1577858483, londonLocation) // 2020-01-01 06:01:23 +00:00
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
|
||||
// during daylight saving time (UTC+1)
|
||||
expectedValue = "+01:00"
|
||||
actualValue = FormatTimezoneOffset(1619845283, londonLocation) // 2021-05-01 06:01:23 +01:00
|
||||
assert.Equal(t, expectedValue, actualValue)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { TransactionReconciliationStatementResponseItem } from '@/models/tr
|
||||
import { isArray } from '@/lib/common.ts';
|
||||
import { sumAmounts } from '@/lib/numeral.ts';
|
||||
import {
|
||||
parseDateTimeFromUnixTime,
|
||||
getGregorianCalendarYearAndMonthFromUnixTime,
|
||||
getYearFirstUnixTimeBySpecifiedUnixTime,
|
||||
getQuarterFirstUnixTimeBySpecifiedUnixTime,
|
||||
@@ -52,11 +53,11 @@ export interface CommonAccountBalanceTrendsChartProps {
|
||||
|
||||
export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTrendsChartProps) {
|
||||
const {
|
||||
formatUnixTimeToShortDate,
|
||||
formatUnixTimeToGregorianLikeShortYear,
|
||||
formatUnixTimeToGregorianLikeShortYearMonth,
|
||||
formatUnixTimeToGregorianLikeYearQuarter,
|
||||
formatUnixTimeToGregorianLikeFiscalYear
|
||||
formatDateTimeToShortDate,
|
||||
formatDateTimeToGregorianLikeShortYear,
|
||||
formatDateTimeToGregorianLikeShortYearMonth,
|
||||
formatDateTimeToGregorianLikeYearQuarter,
|
||||
formatDateTimeToGregorianLikeFiscalYear
|
||||
} = useI18n();
|
||||
|
||||
const dataDateRange = computed<AccountBalanceUnixTimeAndBalanceRange | null>(() => {
|
||||
@@ -150,19 +151,20 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren
|
||||
|
||||
for (const dateRange of allDateRanges.value) {
|
||||
const dataItems = dayDataItemsMap[dateRange.minUnixTime];
|
||||
const minDateTime = parseDateTimeFromUnixTime(dateRange.minUnixTime);
|
||||
|
||||
let displayDate = '';
|
||||
|
||||
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||
displayDate = formatUnixTimeToGregorianLikeShortYear(dateRange.minUnixTime);
|
||||
displayDate = formatDateTimeToGregorianLikeShortYear(minDateTime);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||
displayDate = formatUnixTimeToGregorianLikeFiscalYear(dateRange.minUnixTime);
|
||||
displayDate = formatDateTimeToGregorianLikeFiscalYear(minDateTime);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
||||
displayDate = formatUnixTimeToGregorianLikeYearQuarter(dateRange.minUnixTime);
|
||||
displayDate = formatDateTimeToGregorianLikeYearQuarter(minDateTime);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||
displayDate = formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime);
|
||||
displayDate = formatDateTimeToGregorianLikeShortYearMonth(minDateTime);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type) {
|
||||
displayDate = formatUnixTimeToShortDate(dateRange.minUnixTime);
|
||||
displayDate = formatDateTimeToShortDate(minDateTime);
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
import { type TimeRangeAndDateType, type PresetDateRange, type UnixTimeRange, type WeekDayValue, DateRange } from '@/core/datetime.ts';
|
||||
import {
|
||||
type DateTime,
|
||||
type UnixTimeRange,
|
||||
type TimeRangeAndDateType,
|
||||
type PresetDateRange,
|
||||
type WeekDayValue,
|
||||
DateRange,
|
||||
} from '@/core/datetime.ts';
|
||||
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getUnixTimeFromLocalDatetime,
|
||||
getTodayFirstUnixTime,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getActualUnixTimeForStore,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getSameDateTimeWithCurrentTimezone,
|
||||
getSameDateTimeWithBrowserTimezone,
|
||||
parseDateTimeFromUnixTime,
|
||||
parseDateTimeFromUnixTimeWithBrowserTimezone,
|
||||
getDateRangeByDateType
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
@@ -45,23 +52,21 @@ function getDateRangeFromProps(props: CommonDateRangeSelectionProps): { minDate:
|
||||
}
|
||||
|
||||
export function useDateRangeSelectionBase(props: CommonDateRangeSelectionProps) {
|
||||
const { tt, formatUnixTimeToLongDateTime } = useI18n();
|
||||
const { tt, formatDateTimeToLongDateTime } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const { minDate, maxDate } = getDateRangeFromProps(props);
|
||||
|
||||
const dateRange = ref<Date[]>([
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime(minDate),
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime(maxDate)
|
||||
]);
|
||||
|
||||
const firstDayOfWeek = computed<WeekDayValue>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const beginDateTime = computed<string>(() => {
|
||||
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTimeFromLocalDatetime(dateRange.value[0] as Date), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return formatUnixTimeToLongDateTime(actualBeginUnixTime);
|
||||
return formatDateTimeToLongDateTime(getDateTimeFromSameDateTimeOfLocalDatetime(dateRange.value[0] as Date));
|
||||
});
|
||||
const endDateTime = computed<string>(() => {
|
||||
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTimeFromLocalDatetime(dateRange.value[1] as Date), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return formatUnixTimeToLongDateTime(actualEndUnixTime);
|
||||
return formatDateTimeToLongDateTime(getDateTimeFromSameDateTimeOfLocalDatetime(dateRange.value[1] as Date));
|
||||
});
|
||||
const presetRanges = computed<PresetDateRange[]>(() => {
|
||||
const presetRanges:PresetDateRange[] = [];
|
||||
@@ -82,8 +87,8 @@ export function useDateRangeSelectionBase(props: CommonDateRangeSelectionProps)
|
||||
presetRanges.push({
|
||||
label: tt(dateRangeType.name),
|
||||
value: [
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime(dateRange.minTime),
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime(dateRange.maxTime)
|
||||
]
|
||||
});
|
||||
});
|
||||
@@ -91,6 +96,14 @@ export function useDateRangeSelectionBase(props: CommonDateRangeSelectionProps)
|
||||
return presetRanges;
|
||||
});
|
||||
|
||||
function getLocalDatetimeFromSameDateTimeOfUnixTime(unixTime: number): Date {
|
||||
return getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(parseDateTimeFromUnixTime(unixTime)).getUnixTime());
|
||||
}
|
||||
|
||||
function getDateTimeFromSameDateTimeOfLocalDatetime(localDatetime: Date): DateTime {
|
||||
return getSameDateTimeWithCurrentTimezone(parseDateTimeFromUnixTimeWithBrowserTimezone(getUnixTimeFromLocalDatetime(localDatetime)));
|
||||
}
|
||||
|
||||
function getFinalDateRange(): UnixTimeRange | null {
|
||||
if (!dateRange.value[0] || !dateRange.value[1]) {
|
||||
return null;
|
||||
@@ -99,16 +112,13 @@ export function useDateRangeSelectionBase(props: CommonDateRangeSelectionProps)
|
||||
const currentMinDate = dateRange.value[0];
|
||||
const currentMaxDate = dateRange.value[1];
|
||||
|
||||
let minUnixTime = getUnixTimeFromLocalDatetime(currentMinDate);
|
||||
let maxUnixTime = getUnixTimeFromLocalDatetime(currentMaxDate);
|
||||
const minUnixTime = getDateTimeFromSameDateTimeOfLocalDatetime(currentMinDate).getUnixTime();
|
||||
const maxUnixTime = getDateTimeFromSameDateTimeOfLocalDatetime(currentMaxDate).getUnixTime();
|
||||
|
||||
if (minUnixTime < 0 || maxUnixTime < 0) {
|
||||
throw new Error('Date is too early');
|
||||
}
|
||||
|
||||
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
|
||||
return {
|
||||
minUnixTime,
|
||||
maxUnixTime
|
||||
@@ -123,6 +133,8 @@ export function useDateRangeSelectionBase(props: CommonDateRangeSelectionProps)
|
||||
endDateTime,
|
||||
presetRanges,
|
||||
// functions
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime,
|
||||
getDateTimeFromSameDateTimeOfLocalDatetime,
|
||||
getFinalDateRange
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,15 @@ import { useI18n } from '@/locales/helpers.ts';
|
||||
import { type NameValue } from '@/core/base.ts';
|
||||
import { NumeralSystem } from '@/core/numeral.ts';
|
||||
|
||||
import {
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getUnixTimeFromLocalDatetime,
|
||||
getSameDateTimeWithBrowserTimezone,
|
||||
getSameDateTimeWithTimezoneOffset,
|
||||
parseDateTimeFromUnixTimeWithBrowserTimezone,
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
export interface TimePickerValue {
|
||||
value: string;
|
||||
itemsIndex: number;
|
||||
@@ -30,6 +39,14 @@ export function useDateTimeSelectionBase() {
|
||||
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
|
||||
const meridiemItems = computed<NameValue[]>(() => getAllMeridiemIndicators());
|
||||
|
||||
function getLocalDatetimeFromSameDateTimeOfUnixTime(unixTime: number, utcOffset: number): Date {
|
||||
return getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(parseDateTimeFromUnixTimeWithTimezoneOffset(unixTime, utcOffset)).getUnixTime());
|
||||
}
|
||||
|
||||
function getUnixTimeFromSameDateTimeOfLocalDatetime(localDatetime: Date, utcOffset: number): number {
|
||||
return getSameDateTimeWithTimezoneOffset(parseDateTimeFromUnixTimeWithBrowserTimezone(getUnixTimeFromLocalDatetime(localDatetime)), utcOffset).getUnixTime();
|
||||
}
|
||||
|
||||
function getDisplayTimeValue(value: number, forceTwoDigits: boolean): string {
|
||||
let textualValue = value.toString();
|
||||
|
||||
@@ -89,6 +106,8 @@ export function useDateTimeSelectionBase() {
|
||||
// computed
|
||||
meridiemItems,
|
||||
// functions
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime,
|
||||
getUnixTimeFromSameDateTimeOfLocalDatetime,
|
||||
getDisplayTimeValue,
|
||||
generateAllHours,
|
||||
generateAllMinutesOrSeconds
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getYear0BasedMonthObjectFromString,
|
||||
getYearMonthStringFromYear0BasedMonthObject,
|
||||
getCurrentUnixTime,
|
||||
parseDateTimeFromUnixTime,
|
||||
getThisYearFirstUnixTime,
|
||||
getYearMonthFirstUnixTime,
|
||||
getYearMonthLastUnixTime
|
||||
@@ -49,7 +50,7 @@ function getMonthRangeFromProps(props: CommonMonthRangeSelectionProps): { minDat
|
||||
}
|
||||
|
||||
export function useMonthRangeSelectionBase(props: CommonMonthRangeSelectionProps) {
|
||||
const { formatUnixTimeToGregorianLikeLongYearMonth } = useI18n();
|
||||
const { formatDateTimeToGregorianLikeLongYearMonth } = useI18n();
|
||||
const { minDate, maxDate } = getMonthRangeFromProps(props);
|
||||
|
||||
const dateRange = ref<Year0BasedMonth[]>([
|
||||
@@ -57,8 +58,8 @@ export function useMonthRangeSelectionBase(props: CommonMonthRangeSelectionProps
|
||||
maxDate
|
||||
]);
|
||||
|
||||
const beginDateTime = computed<string>(() => formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthFirstUnixTime(dateRange.value[0] as Year0BasedMonth)));
|
||||
const endDateTime = computed<string>(() => formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthLastUnixTime(dateRange.value[1] as Year0BasedMonth)));
|
||||
const beginDateTime = computed<string>(() => formatDateTimeToGregorianLikeLongYearMonth(parseDateTimeFromUnixTime(getYearMonthFirstUnixTime(dateRange.value[0] as Year0BasedMonth))));
|
||||
const endDateTime = computed<string>(() => formatDateTimeToGregorianLikeLongYearMonth(parseDateTimeFromUnixTime(getYearMonthLastUnixTime(dateRange.value[1] as Year0BasedMonth))));
|
||||
|
||||
function getFinalMonthRange(): { minYearMonth: TextualYearMonth | '', maxYearMonth: TextualYearMonth | '' } | null {
|
||||
if (!dateRange.value[0] || !dateRange.value[1]) {
|
||||
|
||||
@@ -88,9 +88,9 @@ const {
|
||||
getCurrentNumeralSystemType,
|
||||
isLongDateMonthAfterYear,
|
||||
isLongTime24HourFormat,
|
||||
getCalendarDisplayShortYearFromUnixTime,
|
||||
getCalendarDisplayShortMonthFromUnixTime,
|
||||
getCalendarDisplayDayOfMonthFromUnixTime,
|
||||
getCalendarDisplayShortYearFromDateTime,
|
||||
getCalendarDisplayShortMonthFromDateTime,
|
||||
getCalendarDisplayDayOfMonthFromDateTime,
|
||||
getCalendarAlternateDate
|
||||
} = useI18n();
|
||||
|
||||
@@ -138,21 +138,21 @@ function switchView(viewType: MenuView): void {
|
||||
}
|
||||
|
||||
function getDisplayYear(year: number): string {
|
||||
return getCalendarDisplayShortYearFromUnixTime(getYearMonthDayDateTime(year, 1, 1).getUnixTime(), actualNumeralSystem.value);
|
||||
return getCalendarDisplayShortYearFromDateTime(getYearMonthDayDateTime(year, 1, 1), actualNumeralSystem.value);
|
||||
}
|
||||
|
||||
function getDisplayMonth(month: number): string {
|
||||
if (isArray(dateTime.value)) {
|
||||
return getCalendarDisplayShortMonthFromUnixTime(getYearMonthDayDateTime(dateTime.value[0]!.getFullYear(), month + 1, 1).getUnixTime(), actualNumeralSystem.value);
|
||||
return getCalendarDisplayShortMonthFromDateTime(getYearMonthDayDateTime(dateTime.value[0]!.getFullYear(), month + 1, 1), actualNumeralSystem.value);
|
||||
} else if (dateTime.value) {
|
||||
return getCalendarDisplayShortMonthFromUnixTime(getYearMonthDayDateTime(dateTime.value.getFullYear(), month + 1, 1).getUnixTime(), actualNumeralSystem.value);
|
||||
return getCalendarDisplayShortMonthFromDateTime(getYearMonthDayDateTime(dateTime.value.getFullYear(), month + 1, 1), actualNumeralSystem.value);
|
||||
} else {
|
||||
return getCalendarDisplayShortMonthFromUnixTime(getYearMonthDayDateTime(new Date().getFullYear(), month + 1, 1).getUnixTime(), actualNumeralSystem.value);
|
||||
return getCalendarDisplayShortMonthFromDateTime(getYearMonthDayDateTime(new Date().getFullYear(), month + 1, 1), actualNumeralSystem.value);
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplayDay(date: Date): string {
|
||||
return getCalendarDisplayDayOfMonthFromUnixTime(getYearMonthDayDateTime(date.getFullYear(), date.getMonth() + 1, date.getDate()).getUnixTime(), actualNumeralSystem.value);
|
||||
return getCalendarDisplayDayOfMonthFromDateTime(getYearMonthDayDateTime(date.getFullYear(), date.getMonth() + 1, date.getDate()), actualNumeralSystem.value);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
||||
@@ -54,8 +54,8 @@ const emit = defineEmits<{
|
||||
|
||||
const {
|
||||
isLongDateMonthAfterYear,
|
||||
getCalendarDisplayShortYearFromUnixTime,
|
||||
getCalendarDisplayShortMonthFromUnixTime
|
||||
getCalendarDisplayShortYearFromDateTime,
|
||||
getCalendarDisplayShortMonthFromDateTime
|
||||
} = useI18n();
|
||||
|
||||
const yearRange = getAllowedYearRange();
|
||||
@@ -96,14 +96,14 @@ function getYear0BasedMonthFromMonthSelectionValue(value: MonthSelectionValue):
|
||||
}
|
||||
|
||||
function getDisplayYear(year: number): string {
|
||||
return getCalendarDisplayShortYearFromUnixTime(getYearMonthDayDateTime(year, 1, 1).getUnixTime());
|
||||
return getCalendarDisplayShortYearFromDateTime(getYearMonthDayDateTime(year, 1, 1));
|
||||
}
|
||||
|
||||
function getDisplayMonth(month: number): string {
|
||||
if (isArray(dateTime.value)) {
|
||||
return getCalendarDisplayShortMonthFromUnixTime(getYearMonthDayDateTime(dateTime.value[0]!.year, month + 1, 1).getUnixTime());
|
||||
return getCalendarDisplayShortMonthFromDateTime(getYearMonthDayDateTime(dateTime.value[0]!.year, month + 1, 1));
|
||||
} else {
|
||||
return getCalendarDisplayShortMonthFromUnixTime(getYearMonthDayDateTime(dateTime.value.year, month + 1, 1).getUnixTime());
|
||||
return getCalendarDisplayShortMonthFromDateTime(getYearMonthDayDateTime(dateTime.value.year, month + 1, 1));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -38,14 +38,7 @@ import type { CalendarAlternateDate, TextualYearMonthDay, WeekDayValue } from '@
|
||||
import { INCOMPLETE_AMOUNT_SUFFIX } from '@/consts/numeral.ts';
|
||||
|
||||
import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts';
|
||||
import {
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getUnixTimeFromLocalDatetime,
|
||||
getActualUnixTimeForStore,
|
||||
getYearMonthDayDateTime,
|
||||
parseDateTimeFromUnixTime
|
||||
} from '@/lib/datetime.ts';
|
||||
import { getYearMonthDayDateTime } from '@/lib/datetime.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: TextualYearMonthDay | '';
|
||||
@@ -67,7 +60,7 @@ const emit = defineEmits<{
|
||||
const {
|
||||
getAllLongWeekdayNames,
|
||||
getAllShortWeekdayNames,
|
||||
getCalendarDisplayDayOfMonthFromUnixTime,
|
||||
getCalendarDisplayDayOfMonthFromDateTime,
|
||||
getCalendarAlternateDates,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
@@ -105,8 +98,7 @@ const alternateDates = computed<Record<TextualYearMonthDay, string> | undefined>
|
||||
});
|
||||
|
||||
function noTransactionInMonthDay(date: Date): boolean {
|
||||
const dateTime = parseDateTimeFromUnixTime(getActualUnixTimeForStore(getUnixTimeFromLocalDatetime(date), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
return !props.dailyTotalAmounts || !props.dailyTotalAmounts[dateTime.getGregorianCalendarDay()];
|
||||
return !props.dailyTotalAmounts || !props.dailyTotalAmounts[date.getDate()];
|
||||
}
|
||||
|
||||
function getDisplayMonthTotalAmount(amount: number, currency: string | false, symbol: string, incomplete: boolean): string {
|
||||
@@ -115,7 +107,7 @@ function getDisplayMonthTotalAmount(amount: number, currency: string | false, sy
|
||||
}
|
||||
|
||||
function getDisplayDay(date: Date): string {
|
||||
return getCalendarDisplayDayOfMonthFromUnixTime(getYearMonthDayDateTime(date.getFullYear(), date.getMonth() + 1, date.getDate()).getUnixTime());
|
||||
return getCalendarDisplayDayOfMonthFromDateTime(getYearMonthDayDateTime(date.getFullYear(), date.getMonth() + 1, date.getDate()));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -43,13 +43,6 @@ import { type CommonDateRangeSelectionProps, useDateRangeSelectionBase } from '@
|
||||
|
||||
import { ThemeType } from '@/core/theme.ts';
|
||||
|
||||
import {
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
interface DesktopDateRangeSelectionProps extends CommonDateRangeSelectionProps {
|
||||
persistent?: boolean;
|
||||
}
|
||||
@@ -64,7 +57,14 @@ const emit = defineEmits<{
|
||||
const theme = useTheme();
|
||||
|
||||
const { tt } = useI18n();
|
||||
const { dateRange, beginDateTime, endDateTime, presetRanges, getFinalDateRange } = useDateRangeSelectionBase(props);
|
||||
const {
|
||||
dateRange,
|
||||
beginDateTime,
|
||||
endDateTime,
|
||||
presetRanges,
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime,
|
||||
getFinalDateRange
|
||||
} = useDateRangeSelectionBase(props);
|
||||
|
||||
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
|
||||
const showState = computed<boolean>({
|
||||
@@ -94,13 +94,13 @@ function cancel(): void {
|
||||
|
||||
watch(() => props.minTime, (newValue) => {
|
||||
if (newValue) {
|
||||
dateRange.value[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
dateRange.value[0] = getLocalDatetimeFromSameDateTimeOfUnixTime(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.maxTime, (newValue) => {
|
||||
if (newValue) {
|
||||
dateRange.value[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
dateRange.value[1] = getLocalDatetimeFromSameDateTimeOfUnixTime(newValue);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -94,12 +94,9 @@ import {
|
||||
} from '@/core/datetime.ts';
|
||||
import {
|
||||
getHourIn12HourFormat,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getUnixTimeFromLocalDatetime,
|
||||
getActualUnixTimeForStore,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getSameDateTimeWithBrowserTimezone,
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset,
|
||||
parseDateTimeFromKnownDateTimeFormat,
|
||||
getAMOrPM,
|
||||
getCombinedDateAndTimeValues
|
||||
@@ -108,6 +105,7 @@ import { setChildInputFocus } from '@/lib/ui/desktop.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: number;
|
||||
timezoneUtcOffset: number;
|
||||
disabled?: boolean;
|
||||
readonly?: boolean;
|
||||
label?: string;
|
||||
@@ -124,7 +122,7 @@ const {
|
||||
getCurrentNumeralSystemType,
|
||||
parseDateTimeFromLongDateTime,
|
||||
parseDateTimeFromShortDateTime,
|
||||
formatUnixTimeToLongDateTime
|
||||
formatDateTimeToLongDateTime
|
||||
} = useI18n();
|
||||
|
||||
const {
|
||||
@@ -133,6 +131,8 @@ const {
|
||||
isMinuteTwoDigits,
|
||||
isSecondTwoDigits,
|
||||
isMeridiemIndicatorFirst,
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime,
|
||||
getUnixTimeFromSameDateTimeOfLocalDatetime,
|
||||
getDisplayTimeValue,
|
||||
generateAllHours,
|
||||
generateAllMinutesOrSeconds
|
||||
@@ -147,10 +147,10 @@ const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType(
|
||||
|
||||
const dateTime = computed<Date>({
|
||||
get: () => {
|
||||
return getLocalDatetimeFromUnixTime(props.modelValue);
|
||||
return getLocalDatetimeFromSameDateTimeOfUnixTime(props.modelValue, props.timezoneUtcOffset);
|
||||
},
|
||||
set: (value: Date) => {
|
||||
const unixTime = getUnixTimeFromLocalDatetime(value);
|
||||
const unixTime = getUnixTimeFromSameDateTimeOfLocalDatetime(value, props.timezoneUtcOffset);
|
||||
|
||||
if (unixTime < 0) {
|
||||
emit('error', 'Date is too early');
|
||||
@@ -161,7 +161,7 @@ const dateTime = computed<Date>({
|
||||
}
|
||||
});
|
||||
|
||||
const displayTime = computed<string>(() => formatUnixTimeToLongDateTime(getActualUnixTimeForStore(getUnixTimeFromLocalDatetime(dateTime.value), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())));
|
||||
const displayTime = computed<string>(() => formatDateTimeToLongDateTime(parseDateTimeFromUnixTimeWithTimezoneOffset(props.modelValue, props.timezoneUtcOffset)));
|
||||
|
||||
const hourItems = computed<TimePickerValue[]>(() => generateAllHours(1, isHourTwoDigits.value));
|
||||
const minuteItems = computed<TimePickerValue[]>(() => generateAllMinutesOrSeconds(1, isMinuteTwoDigits.value));
|
||||
@@ -252,7 +252,7 @@ function onPaste(event: ClipboardEvent): void {
|
||||
dt = parseDateTimeFromKnownDateTimeFormat(text, formats[0] as KnownDateTimeFormat);
|
||||
|
||||
if (dt) {
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dt.getUnixTime(), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(dt).getUnixTime());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -260,14 +260,14 @@ function onPaste(event: ClipboardEvent): void {
|
||||
dt = parseDateTimeFromLongDateTime(text);
|
||||
|
||||
if (dt) {
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dt.getUnixTime(), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(dt).getUnixTime());
|
||||
return;
|
||||
}
|
||||
|
||||
dt = parseDateTimeFromShortDateTime(text);
|
||||
|
||||
if (dt) {
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dt.getUnixTime(), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(dt).getUnixTime());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
isNumber
|
||||
} from '@/lib/common.ts';
|
||||
import {
|
||||
parseDateTimeFromUnixTime,
|
||||
getYearMonthFirstUnixTime,
|
||||
getYearMonthLastUnixTime,
|
||||
getDateTypeByDateRange,
|
||||
@@ -95,11 +96,11 @@ const theme = useTheme();
|
||||
const {
|
||||
tt,
|
||||
getCurrentLanguageTextDirection,
|
||||
formatUnixTimeToShortDate,
|
||||
formatUnixTimeToGregorianLikeShortYear,
|
||||
formatUnixTimeToGregorianLikeShortYearMonth,
|
||||
formatDateTimeToShortDate,
|
||||
formatDateTimeToGregorianLikeShortYear,
|
||||
formatDateTimeToGregorianLikeShortYearMonth,
|
||||
formatYearQuarterToGregorianLikeYearQuarter,
|
||||
formatUnixTimeToGregorianLikeFiscalYear,
|
||||
formatDateTimeToGregorianLikeFiscalYear,
|
||||
formatAmountToWesternArabicNumeralsWithoutDigitGrouping,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
@@ -151,16 +152,18 @@ const allDisplayDateRanges = computed<string[]>(() => {
|
||||
const allDisplayDateRanges: string[] = [];
|
||||
|
||||
for (const dateRange of allDateRanges.value) {
|
||||
const minDateTime = parseDateTimeFromUnixTime(dateRange.minUnixTime);
|
||||
|
||||
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||
allDisplayDateRanges.push(formatUnixTimeToGregorianLikeShortYear(dateRange.minUnixTime));
|
||||
allDisplayDateRanges.push(formatDateTimeToGregorianLikeShortYear(minDateTime));
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type && 'year' in dateRange) {
|
||||
allDisplayDateRanges.push(formatUnixTimeToGregorianLikeFiscalYear(dateRange.minUnixTime));
|
||||
allDisplayDateRanges.push(formatDateTimeToGregorianLikeFiscalYear(minDateTime));
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type && 'quarter' in dateRange) {
|
||||
allDisplayDateRanges.push(formatYearQuarterToGregorianLikeYearQuarter(dateRange.year, dateRange.quarter));
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||
allDisplayDateRanges.push(formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime));
|
||||
allDisplayDateRanges.push(formatDateTimeToGregorianLikeShortYearMonth(minDateTime));
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type && props.chartMode === 'daily') {
|
||||
allDisplayDateRanges.push(formatUnixTimeToShortDate(dateRange.minUnixTime));
|
||||
allDisplayDateRanges.push(formatDateTimeToShortDate(minDateTime));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,13 +45,6 @@ import { type CommonDateRangeSelectionProps, useDateRangeSelectionBase } from '@
|
||||
|
||||
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
||||
|
||||
import {
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
type DateTimePickerType = InstanceType<typeof DateTimePicker>;
|
||||
|
||||
const props = defineProps<CommonDateRangeSelectionProps>();
|
||||
@@ -62,7 +55,14 @@ const emit = defineEmits<{
|
||||
|
||||
const { tt } = useI18n();
|
||||
const { showToast } = useI18nUIComponents();
|
||||
const { dateRange, beginDateTime, endDateTime, presetRanges, getFinalDateRange } = useDateRangeSelectionBase(props);
|
||||
const {
|
||||
dateRange,
|
||||
beginDateTime,
|
||||
endDateTime,
|
||||
presetRanges,
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime,
|
||||
getFinalDateRange
|
||||
} = useDateRangeSelectionBase(props);
|
||||
|
||||
const environmentsStore = useEnvironmentsStore();
|
||||
|
||||
@@ -91,11 +91,11 @@ function cancel(): void {
|
||||
|
||||
function onSheetOpen(): void {
|
||||
if (props.minTime) {
|
||||
dateRange.value[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(props.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
dateRange.value[0] = getLocalDatetimeFromSameDateTimeOfUnixTime(props.minTime);
|
||||
}
|
||||
|
||||
if (props.maxTime) {
|
||||
dateRange.value[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(props.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
dateRange.value[1] = getLocalDatetimeFromSameDateTimeOfUnixTime(props.maxTime);
|
||||
}
|
||||
|
||||
window.dispatchEvent(new Event('resize')); // fix vue-datepicker preset max-width
|
||||
|
||||
@@ -111,9 +111,7 @@ import { NumeralSystem } from '@/core/numeral.ts';
|
||||
import { isDefined } from '@/lib/common.ts';
|
||||
import {
|
||||
getHourIn12HourFormat,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getCurrentUnixTime,
|
||||
getUnixTimeFromLocalDatetime,
|
||||
getAMOrPM,
|
||||
getCombinedDateAndTimeValues
|
||||
} from '@/lib/datetime.ts';
|
||||
@@ -122,6 +120,7 @@ type DateTimePickerType = InstanceType<typeof DateTimePicker>;
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: number;
|
||||
timezoneUtcOffset: number;
|
||||
initMode?: string;
|
||||
show: boolean;
|
||||
}>();
|
||||
@@ -144,6 +143,8 @@ const {
|
||||
isSecondTwoDigits,
|
||||
isMeridiemIndicatorFirst,
|
||||
meridiemItems,
|
||||
getLocalDatetimeFromSameDateTimeOfUnixTime,
|
||||
getUnixTimeFromSameDateTimeOfLocalDatetime,
|
||||
getDisplayTimeValue,
|
||||
generateAllHours,
|
||||
generateAllMinutesOrSeconds
|
||||
@@ -160,7 +161,7 @@ let resetTimePickerItemPositionItemsLastOffsetTop: number | undefined = undefine
|
||||
let resetTimePickerItemPositionCheckedFrames: number | undefined = undefined;
|
||||
|
||||
const mode = ref<string>(props.initMode || 'time');
|
||||
const dateTime = ref<Date>(getLocalDatetimeFromUnixTime(props.modelValue || getCurrentUnixTime()));
|
||||
const dateTime = ref<Date>(getLocalDatetimeFromSameDateTimeOfUnixTime(props.modelValue || getCurrentUnixTime(), props.timezoneUtcOffset));
|
||||
const timePickerContainerHeight = ref<number | undefined>(undefined);
|
||||
const timePickerItemHeight = ref<number | undefined>(undefined);
|
||||
|
||||
@@ -213,7 +214,7 @@ function switchMode(): void {
|
||||
}
|
||||
|
||||
function setCurrentTime(): void {
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(getCurrentUnixTime());
|
||||
dateTime.value = getLocalDatetimeFromSameDateTimeOfUnixTime(getCurrentUnixTime(), props.timezoneUtcOffset);
|
||||
|
||||
if (mode.value === 'time') {
|
||||
scrollAllTimeSelectedItems();
|
||||
@@ -225,7 +226,7 @@ function confirm(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
const unixTime = getUnixTimeFromLocalDatetime(dateTime.value);
|
||||
const unixTime = getUnixTimeFromSameDateTimeOfLocalDatetime(dateTime.value, props.timezoneUtcOffset);
|
||||
|
||||
if (unixTime < 0) {
|
||||
showToast('Date is too early');
|
||||
@@ -420,7 +421,7 @@ function onSheetOpen(): void {
|
||||
mode.value = props.initMode || 'time';
|
||||
|
||||
if (props.modelValue) {
|
||||
dateTime.value = getLocalDatetimeFromUnixTime(props.modelValue);
|
||||
dateTime.value = getLocalDatetimeFromSameDateTimeOfUnixTime(props.modelValue, props.timezoneUtcOffset);
|
||||
}
|
||||
|
||||
if (mode.value === 'time') {
|
||||
|
||||
@@ -144,6 +144,7 @@ import {
|
||||
isNumber
|
||||
} from '@/lib/common.ts';
|
||||
import {
|
||||
parseDateTimeFromUnixTime,
|
||||
getYearMonthFirstUnixTime,
|
||||
getYearMonthLastUnixTime,
|
||||
getDateTypeByDateRange,
|
||||
@@ -200,11 +201,11 @@ const emit = defineEmits<{
|
||||
|
||||
const {
|
||||
tt,
|
||||
formatUnixTimeToShortDate,
|
||||
formatUnixTimeToGregorianLikeShortYear,
|
||||
formatUnixTimeToGregorianLikeShortYearMonth,
|
||||
formatDateTimeToShortDate,
|
||||
formatDateTimeToGregorianLikeShortYear,
|
||||
formatDateTimeToGregorianLikeShortYearMonth,
|
||||
formatYearQuarterToGregorianLikeYearQuarter,
|
||||
formatUnixTimeToGregorianLikeFiscalYear,
|
||||
formatDateTimeToGregorianLikeFiscalYear,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
@@ -324,18 +325,19 @@ const allDisplayDataItems = computed<TrendsBarChartData>(() => {
|
||||
dateRangeKey = `${dateRange.year}-${dateRange.month}-${dateRange.day}`;
|
||||
}
|
||||
|
||||
const minDateTime = parseDateTimeFromUnixTime(dateRange.minUnixTime);
|
||||
let displayDateRange = '';
|
||||
|
||||
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||
displayDateRange = formatUnixTimeToGregorianLikeShortYear(dateRange.minUnixTime);
|
||||
displayDateRange = formatDateTimeToGregorianLikeShortYear(minDateTime);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||
displayDateRange = formatUnixTimeToGregorianLikeFiscalYear(dateRange.minUnixTime);
|
||||
displayDateRange = formatDateTimeToGregorianLikeFiscalYear(minDateTime);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type && 'quarter' in dateRange) {
|
||||
displayDateRange = formatYearQuarterToGregorianLikeYearQuarter(dateRange.year, dateRange.quarter);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||
displayDateRange = formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime);
|
||||
displayDateRange = formatDateTimeToGregorianLikeShortYearMonth(minDateTime);
|
||||
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type && props.chartMode === 'daily') {
|
||||
displayDateRange = formatUnixTimeToShortDate(dateRange.minUnixTime);
|
||||
displayDateRange = formatDateTimeToShortDate(minDateTime);
|
||||
}
|
||||
|
||||
const dataItems = allDateRangeItemsMap[dateRangeKey] || [];
|
||||
|
||||
+90
-27
@@ -69,7 +69,31 @@ interface DateTimeFormatResult {
|
||||
hasNumeral?: boolean;
|
||||
}
|
||||
|
||||
type DateTimeTokenFormatFunction = (d: MomentDateTime, options: DateTimeFormatOptions) => DateTimeFormatResult
|
||||
type DateTimeTokenFormatFunction = (d: MomentDateTime, options: DateTimeFormatOptions) => DateTimeFormatResult;
|
||||
|
||||
const westernmostTimezoneUtcOffset: number = -720; // Etc/GMT+12 (UTC-12:00)
|
||||
const easternmostTimezoneUtcOffset: number = 840; // Pacific/Kiritimati (UTC+14:00)
|
||||
|
||||
function getFixedTimezoneName(utcOffset: number): string {
|
||||
return `Fixed/Timezone${utcOffset}`;
|
||||
}
|
||||
|
||||
(function initFixedTimezone(): void {
|
||||
for (let utcOffset = westernmostTimezoneUtcOffset; utcOffset <= easternmostTimezoneUtcOffset; utcOffset += 15) {
|
||||
const timezoneName = getFixedTimezoneName(utcOffset);
|
||||
|
||||
if (moment.tz.zone(timezoneName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
moment.tz.add(moment.tz.pack({
|
||||
name: timezoneName,
|
||||
abbrs: [`FIX${utcOffset}`],
|
||||
offsets: [-utcOffset],
|
||||
untils: [0]
|
||||
}));
|
||||
}
|
||||
})();
|
||||
|
||||
class MomentDateTime implements DateTime {
|
||||
private static readonly tokenFormatFuncs: Record<string, DateTimeTokenFormatFunction> = {
|
||||
@@ -501,28 +525,29 @@ export function getUtcOffsetByUtcOffsetMinutes(utcOffsetMinutes: number): string
|
||||
}
|
||||
}
|
||||
|
||||
export function getTimezoneOffset(timezone?: string): string {
|
||||
return getUtcOffsetByUtcOffsetMinutes(getTimezoneOffsetMinutes(timezone));
|
||||
export function getTimezoneOffset(unixTime: number, timezone?: string): string {
|
||||
return getUtcOffsetByUtcOffsetMinutes(getTimezoneOffsetMinutes(unixTime, timezone));
|
||||
}
|
||||
|
||||
export function getTimezoneOffsetMinutes(timezone?: string): number {
|
||||
export function getTimezoneOffsetMinutes(unixTime: number, timezone?: string): number {
|
||||
if (timezone) {
|
||||
return moment().tz(timezone).utcOffset();
|
||||
return moment.unix(unixTime).tz(timezone).utcOffset();
|
||||
} else {
|
||||
return moment().utcOffset();
|
||||
return moment.unix(unixTime).utcOffset();
|
||||
}
|
||||
}
|
||||
|
||||
export function getBrowserTimezoneOffset(): string {
|
||||
return getUtcOffsetByUtcOffsetMinutes(getBrowserTimezoneOffsetMinutes());
|
||||
export function getBrowserTimezoneOffset(unixTime: number): string {
|
||||
return getUtcOffsetByUtcOffsetMinutes(getBrowserTimezoneOffsetMinutes(unixTime));
|
||||
}
|
||||
|
||||
export function getBrowserTimezoneOffsetMinutes(): number {
|
||||
return -new Date().getTimezoneOffset();
|
||||
export function getBrowserTimezoneOffsetMinutes(unixTime: number): number {
|
||||
const date = getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(parseDateTimeFromUnixTime(unixTime)).getUnixTime());
|
||||
return -date.getTimezoneOffset();
|
||||
}
|
||||
|
||||
export function guessTimezoneName(): string {
|
||||
return moment.tz.guess(true);
|
||||
export function getBrowserTimezoneName(): string {
|
||||
return new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
}
|
||||
|
||||
export function getLocalDatetimeFromUnixTime(unixTime: number): Date {
|
||||
@@ -533,12 +558,46 @@ export function getUnixTimeFromLocalDatetime(datetime: Date): number {
|
||||
return Math.floor(datetime.getTime() / 1000);
|
||||
}
|
||||
|
||||
export function getActualUnixTimeForStore(unixTime: number, utcOffset: number, currentUtcOffset: number): number {
|
||||
return unixTime - (utcOffset - currentUtcOffset) * 60;
|
||||
export function getSameDateTimeWithCurrentTimezone(dateTime: DateTime): DateTime {
|
||||
const newDateTime = moment().set({
|
||||
year: dateTime.getGregorianCalendarYear(),
|
||||
month: dateTime.getGregorianCalendarMonth() - 1,
|
||||
date: dateTime.getGregorianCalendarDay(),
|
||||
hour: dateTime.getHour(),
|
||||
minute: dateTime.getMinute(),
|
||||
second: dateTime.getSecond(),
|
||||
millisecond: 0
|
||||
});
|
||||
|
||||
return MomentDateTime.of(newDateTime);
|
||||
}
|
||||
|
||||
export function getDummyUnixTimeForLocalUsage(unixTime: number, utcOffset: number, currentUtcOffset: number): number {
|
||||
return unixTime + (utcOffset - currentUtcOffset) * 60;
|
||||
export function getSameDateTimeWithBrowserTimezone(dateTime: DateTime): DateTime {
|
||||
const newDateTime = moment().tz(getBrowserTimezoneName()).set({
|
||||
year: dateTime.getGregorianCalendarYear(),
|
||||
month: dateTime.getGregorianCalendarMonth() - 1,
|
||||
date: dateTime.getGregorianCalendarDay(),
|
||||
hour: dateTime.getHour(),
|
||||
minute: dateTime.getMinute(),
|
||||
second: dateTime.getSecond(),
|
||||
millisecond: 0,
|
||||
});
|
||||
|
||||
return MomentDateTime.of(newDateTime);
|
||||
}
|
||||
|
||||
export function getSameDateTimeWithTimezoneOffset(dateTime: DateTime, utcOffset: number): DateTime {
|
||||
const newDateTime = moment().tz(getFixedTimezoneName(utcOffset)).set({
|
||||
year: dateTime.getGregorianCalendarYear(),
|
||||
month: dateTime.getGregorianCalendarMonth() - 1,
|
||||
date: dateTime.getGregorianCalendarDay(),
|
||||
hour: dateTime.getHour(),
|
||||
minute: dateTime.getMinute(),
|
||||
second: dateTime.getSecond(),
|
||||
millisecond: 0
|
||||
});
|
||||
|
||||
return MomentDateTime.of(newDateTime);
|
||||
}
|
||||
|
||||
export function getCurrentDateTime(): DateTime {
|
||||
@@ -554,18 +613,18 @@ export function getYearMonthDayDateTime(year: number, month: number, day: number
|
||||
return MomentDateTime.of(date);
|
||||
}
|
||||
|
||||
export function parseDateTimeFromUnixTime(unixTime: number, utcOffset?: number, currentUtcOffset?: number): DateTime {
|
||||
if (isNumber(utcOffset)) {
|
||||
if (!isNumber(currentUtcOffset)) {
|
||||
currentUtcOffset = getTimezoneOffsetMinutes();
|
||||
}
|
||||
|
||||
unixTime = getDummyUnixTimeForLocalUsage(unixTime, utcOffset, currentUtcOffset);
|
||||
}
|
||||
|
||||
export function parseDateTimeFromUnixTime(unixTime: number): DateTime {
|
||||
return MomentDateTime.of(moment.unix(unixTime));
|
||||
}
|
||||
|
||||
export function parseDateTimeFromUnixTimeWithBrowserTimezone(unixTime: number): DateTime {
|
||||
return MomentDateTime.of(moment.unix(unixTime).tz(getBrowserTimezoneName()));
|
||||
}
|
||||
|
||||
export function parseDateTimeFromUnixTimeWithTimezoneOffset(unixTime: number, utcOffset: number): DateTime {
|
||||
return MomentDateTime.of(moment.unix(unixTime).tz(getFixedTimezoneName(utcOffset)));
|
||||
}
|
||||
|
||||
export function parseDateTimeFromKnownDateTimeFormat(dateTime: string, format: KnownDateTimeFormat): DateTime | undefined {
|
||||
const m = moment(dateTime, format.format);
|
||||
|
||||
@@ -586,8 +645,12 @@ export function parseDateTimeFromString(dateTime: string, format: string): DateT
|
||||
return MomentDateTime.of(m);
|
||||
}
|
||||
|
||||
export function formatUnixTime(unixTime: number, format: string, options: DateTimeFormatOptions, utcOffset?: number, currentUtcOffset?: number): string {
|
||||
return parseDateTimeFromUnixTime(unixTime, utcOffset, currentUtcOffset).format(format, options);
|
||||
export function formatDateTime(dateTime: DateTime, format: string, options: DateTimeFormatOptions): string {
|
||||
return dateTime.format(format, options);
|
||||
}
|
||||
|
||||
export function formatUnixTime(unixTime: number, format: string, options: DateTimeFormatOptions): string {
|
||||
return parseDateTimeFromUnixTime(unixTime).format(format, options);
|
||||
}
|
||||
|
||||
export function formatCurrentTime(format: string, options: DateTimeFormatOptions): string {
|
||||
|
||||
+4
-3
@@ -178,7 +178,8 @@ import {
|
||||
} from './server_settings.ts';
|
||||
import {
|
||||
getTimezoneOffsetMinutes,
|
||||
guessTimezoneName
|
||||
getBrowserTimezoneName,
|
||||
getCurrentUnixTime
|
||||
} from './datetime.ts';
|
||||
import { generateRandomUUID } from './misc.ts';
|
||||
import { getBasePath } from './web.ts';
|
||||
@@ -208,12 +209,12 @@ axios.interceptors.request.use((config: ApiRequestConfig) => {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
config.headers['X-Timezone-Offset'] = getTimezoneOffsetMinutes();
|
||||
config.headers['X-Timezone-Offset'] = getTimezoneOffsetMinutes(getCurrentUnixTime());
|
||||
|
||||
let timezoneName = getTimeZone();
|
||||
|
||||
if (!timezoneName || timezoneName.trim().length < 1) {
|
||||
timezoneName = guessTimezoneName();
|
||||
timezoneName = getBrowserTimezoneName();
|
||||
}
|
||||
|
||||
config.headers['X-Timezone-Name'] = timezoneName;
|
||||
|
||||
+5
-10
@@ -11,8 +11,7 @@ import {
|
||||
isNumber
|
||||
} from './common.ts';
|
||||
import {
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getDummyUnixTimeForLocalUsage
|
||||
getTimezoneOffsetMinutes
|
||||
} from './datetime.ts';
|
||||
import {
|
||||
categoryTypeToTransactionType,
|
||||
@@ -33,9 +32,10 @@ export interface SetTransactionOptions {
|
||||
comment?: string;
|
||||
}
|
||||
|
||||
export function setTransactionModelByTransaction(transaction: Transaction, transaction2: Transaction | null | undefined, allCategories: Record<number, TransactionCategory[]>, allCategoriesMap: Record<string, TransactionCategory>, allVisibleAccounts: Account[], allAccountsMap: Record<string, Account>, allTagsMap: Record<string, TransactionTag>, defaultAccountId: string, options: SetTransactionOptions, setContextData: boolean, convertContextTime: boolean): void {
|
||||
export function setTransactionModelByTransaction(transaction: Transaction, transaction2: Transaction | null | undefined, allCategories: Record<number, TransactionCategory[]>, allCategoriesMap: Record<string, TransactionCategory>, allVisibleAccounts: Account[], allAccountsMap: Record<string, Account>, allTagsMap: Record<string, TransactionTag>, defaultAccountId: string, options: SetTransactionOptions, setContextData: boolean): void {
|
||||
if (isDefined(options.time)) {
|
||||
transaction.time = options.time;
|
||||
transaction.utcOffset = getTimezoneOffsetMinutes(transaction.time, transaction.timeZone);
|
||||
}
|
||||
|
||||
if (!options.type && options.categoryId && options.categoryId !== '0' && allCategoriesMap[options.categoryId]) {
|
||||
@@ -172,14 +172,9 @@ export function setTransactionModelByTransaction(transaction: Transaction, trans
|
||||
}
|
||||
|
||||
if (setContextData) {
|
||||
transaction.utcOffset = transaction2.utcOffset;
|
||||
transaction.time = transaction2.time;
|
||||
transaction.timeZone = transaction2.timeZone;
|
||||
|
||||
if (convertContextTime) {
|
||||
transaction.time = getDummyUnixTimeForLocalUsage(transaction2.time, transaction.utcOffset, getBrowserTimezoneOffsetMinutes());
|
||||
} else {
|
||||
transaction.time = transaction2.time;
|
||||
}
|
||||
transaction.utcOffset = transaction2.utcOffset;
|
||||
}
|
||||
|
||||
transaction.sourceAccountId = transaction2.sourceAccountId;
|
||||
|
||||
+37
-45
@@ -187,6 +187,7 @@ import {
|
||||
formatCurrentTime,
|
||||
formatGregorianCalendarYearDashMonthDashDay,
|
||||
formatGregorianCalendarMonthDashDay,
|
||||
formatDateTime,
|
||||
formatUnixTime,
|
||||
getBrowserTimezoneOffset,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
@@ -202,7 +203,7 @@ import {
|
||||
getTimeDifferenceHoursAndMinutes,
|
||||
getTimezoneOffset,
|
||||
getTimezoneOffsetMinutes,
|
||||
guessTimezoneName,
|
||||
getBrowserTimezoneName,
|
||||
isDateRangeMatchFullMonths,
|
||||
isDateRangeMatchFullYears,
|
||||
isPM
|
||||
@@ -1159,20 +1160,20 @@ export function useI18n() {
|
||||
return allRecentMonthDateRanges;
|
||||
}
|
||||
|
||||
function getAllTimezones(includeSystemDefault?: boolean): LocalizedTimezoneInfo[] {
|
||||
function getAllTimezones(unixTime: number, includeSystemDefault?: boolean): LocalizedTimezoneInfo[] {
|
||||
const numeralSystem = getCurrentNumeralSystemType();
|
||||
const defaultTimezoneOffset = numeralSystem.replaceWesternArabicDigitsToLocalizedDigits(getBrowserTimezoneOffset());
|
||||
const defaultTimezoneOffsetMinutes = getBrowserTimezoneOffsetMinutes();
|
||||
const defaultTimezoneOffset = numeralSystem.replaceWesternArabicDigitsToLocalizedDigits(getBrowserTimezoneOffset(unixTime));
|
||||
const defaultTimezoneOffsetMinutes = getBrowserTimezoneOffsetMinutes(unixTime);
|
||||
const allTimezoneInfos: LocalizedTimezoneInfo[] = [];
|
||||
|
||||
for (const timezoneInfo of ALL_TIMEZONES) {
|
||||
const utcOffset = (timezoneInfo.timezoneName !== UTC_TIMEZONE.timezoneName ? numeralSystem.replaceWesternArabicDigitsToLocalizedDigits(getTimezoneOffset(timezoneInfo.timezoneName)) : '');
|
||||
const utcOffset = (timezoneInfo.timezoneName !== UTC_TIMEZONE.timezoneName ? numeralSystem.replaceWesternArabicDigitsToLocalizedDigits(getTimezoneOffset(unixTime, timezoneInfo.timezoneName)) : '');
|
||||
const displayName = t(`timezone.${timezoneInfo.displayName}`);
|
||||
|
||||
allTimezoneInfos.push({
|
||||
name: timezoneInfo.timezoneName,
|
||||
utcOffset: utcOffset,
|
||||
utcOffsetMinutes: getTimezoneOffsetMinutes(timezoneInfo.timezoneName),
|
||||
utcOffsetMinutes: getTimezoneOffsetMinutes(unixTime, timezoneInfo.timezoneName),
|
||||
displayName: displayName,
|
||||
displayNameWithUtcOffset: `(UTC${utcOffset}) ${displayName}`
|
||||
});
|
||||
@@ -1206,7 +1207,7 @@ export function useI18n() {
|
||||
|
||||
function getAllTimezoneTypesUsedForStatistics(currentTimezone?: string): TypeAndDisplayName[] {
|
||||
const numeralSystem = getCurrentNumeralSystemType();
|
||||
const currentTimezoneOffset = numeralSystem.replaceWesternArabicDigitsToLocalizedDigits(getTimezoneOffset(currentTimezone));
|
||||
const currentTimezoneOffset = numeralSystem.replaceWesternArabicDigitsToLocalizedDigits(getTimezoneOffset(getCurrentUnixTime(), currentTimezone));
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -1789,12 +1790,11 @@ export function useI18n() {
|
||||
return formatGregorianCalendarMonthDashDay(monthDay, getLocalizedLongMonthDayFormat(), getDateTimeFormatOptions({ calendarType: gregorianLikeCalendarType, numeralSystem: numeralSystem }));
|
||||
}
|
||||
|
||||
function formatUnixTimeToGregorianLikeYearQuarter(unixTime: number): string {
|
||||
function formatDateTimeToGregorianLikeYearQuarter(dateTime: DateTime): string {
|
||||
const gregorianLikeCalendarType = getGregorianLikeCalendarType();
|
||||
const dateTimeFormatOptions = getDateTimeFormatOptions({ calendarType: gregorianLikeCalendarType });
|
||||
const date = parseDateTimeFromUnixTime(unixTime);
|
||||
const year = date.getLocalizedCalendarYear(dateTimeFormatOptions);
|
||||
const quarter = date.getLocalizedCalendarQuarter(dateTimeFormatOptions);
|
||||
const year = dateTime.getLocalizedCalendarYear(dateTimeFormatOptions);
|
||||
const quarter = dateTime.getLocalizedCalendarQuarter(dateTimeFormatOptions);
|
||||
return formatYearQuarter(year, quarter);
|
||||
}
|
||||
|
||||
@@ -1887,9 +1887,9 @@ export function useI18n() {
|
||||
return `${displayStartTime} ~ ${displayEndTime}`;
|
||||
}
|
||||
|
||||
function getTimezoneDifferenceDisplayText(utcOffset: number): string {
|
||||
function getTimezoneDifferenceDisplayText(unixTime: number, utcOffset: number): string {
|
||||
const numeralSystem = getCurrentNumeralSystemType();
|
||||
const defaultTimezoneOffset = getTimezoneOffsetMinutes();
|
||||
const defaultTimezoneOffset = getTimezoneOffsetMinutes(unixTime);
|
||||
const offsetTime = getTimeDifferenceHoursAndMinutes(utcOffset - defaultTimezoneOffset);
|
||||
|
||||
if (utcOffset > defaultTimezoneOffset) {
|
||||
@@ -2284,19 +2284,11 @@ export function useI18n() {
|
||||
}
|
||||
|
||||
function setTimeZone(timezone: string): void {
|
||||
let timezoneOffsetMinutes = getBrowserTimezoneOffsetMinutes();
|
||||
|
||||
if (timezone) {
|
||||
timezoneOffsetMinutes = getTimezoneOffsetMinutes(timezone);
|
||||
moment.tz.setDefault(timezone);
|
||||
} else {
|
||||
moment.tz.setDefault();
|
||||
}
|
||||
|
||||
moment.tz.add(moment.tz.pack({
|
||||
name: 'Fixed/Timezone',
|
||||
abbrs: ['FIX'],
|
||||
offsets: [-timezoneOffsetMinutes],
|
||||
untils: [0]
|
||||
}));
|
||||
moment.tz.setDefault('Fixed/Timezone');
|
||||
}
|
||||
|
||||
function initLocale(lastUserLanguage?: string, timezone?: string): LocaleDefaultSettings | null {
|
||||
@@ -2317,7 +2309,7 @@ export function useI18n() {
|
||||
logger.info(`Current timezone is ${timezone}`);
|
||||
setTimeZone(timezone);
|
||||
} else {
|
||||
logger.info(`No timezone is set, use browser default ${getTimezoneOffset()} (maybe ${guessTimezoneName()})`);
|
||||
logger.info(`No timezone is set, use browser default ${getTimezoneOffset(getCurrentUnixTime())} (${getBrowserTimezoneName()})`);
|
||||
setTimeZone('');
|
||||
}
|
||||
|
||||
@@ -2423,36 +2415,36 @@ export function useI18n() {
|
||||
isLongTimeMinuteTwoDigits,
|
||||
isLongTimeSecondTwoDigits,
|
||||
// format date time (by calendar display type) functions
|
||||
getCalendarDisplayShortYearFromUnixTime: (unixTime: number, numeralSystem?: NumeralSystem, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortYearFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem }), utcOffset, currentUtcOffset),
|
||||
getCalendarDisplayShortMonthFromUnixTime: (unixTime: number, numeralSystem?: NumeralSystem, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, 'MMM', getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem }), utcOffset, currentUtcOffset),
|
||||
getCalendarDisplayDayOfMonthFromUnixTime: (unixTime: number, numeralSystem?: NumeralSystem, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortDayFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem }), utcOffset, currentUtcOffset),
|
||||
getCalendarDisplayShortYearFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, getLocalizedShortYearFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
||||
getCalendarDisplayShortMonthFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, 'MMM', getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
||||
getCalendarDisplayDayOfMonthFromDateTime: (dateTime: DateTime, numeralSystem?: NumeralSystem) => formatDateTime(dateTime, getLocalizedShortDayFormat(), getDateTimeFormatOptions({ calendarType: getCurrentCalendarDisplayType().primaryCalendarType, numeralSystem: numeralSystem })),
|
||||
// format date time (by date display type) functions
|
||||
parseDateTimeFromLongDateTime: (dateTime: string) => parseDateTimeFromString(dateTime, getLocalizedLongDateFormat() + ' ' + getLocalizedLongTimeFormat()),
|
||||
parseDateTimeFromShortDateTime: (dateTime: string) => parseDateTimeFromString(dateTime, getLocalizedShortDateFormat() + ' ' + getLocalizedShortTimeFormat()),
|
||||
formatUnixTimeToLongDateTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongDateFormat() + ' ' + getLocalizedLongTimeFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortDateTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortDateFormat() + ' ' + getLocalizedShortTimeFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongDate: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongDateFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortDate: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortDateFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongMonthDay: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongMonthDayFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortMonthDay: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortMonthDayFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongTimeFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortTimeFormat(), getDateTimeFormatOptions(), utcOffset, currentUtcOffset),
|
||||
formatDateTimeToLongDateTime: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedLongDateFormat() + ' ' + getLocalizedLongTimeFormat(), getDateTimeFormatOptions()),
|
||||
formatDateTimeToShortDateTime: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedShortDateFormat() + ' ' + getLocalizedShortTimeFormat(), getDateTimeFormatOptions()),
|
||||
formatDateTimeToLongDate: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedLongDateFormat(), getDateTimeFormatOptions()),
|
||||
formatDateTimeToShortDate: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedShortDateFormat(), getDateTimeFormatOptions()),
|
||||
formatDateTimeToLongMonthDay: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedLongMonthDayFormat(), getDateTimeFormatOptions()),
|
||||
formatDateTimeToShortMonthDay: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedShortMonthDayFormat(), getDateTimeFormatOptions()),
|
||||
formatDateTimeToLongTime: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedLongTimeFormat(), getDateTimeFormatOptions()),
|
||||
formatDateTimeToShortTime: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedShortTimeFormat(), getDateTimeFormatOptions()),
|
||||
formatGregorianTextualYearMonthDayToLongDate: (date: TextualYearMonthDay) => formatGregorianCalendarYearDashMonthDashDay(date, getLocalizedLongDateFormat(), getDateTimeFormatOptions()),
|
||||
// format date time (Gregorian calendar and Gregorian-like calendar) functions
|
||||
formatUnixTimeToGregorianLikeLongYear: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongYearFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() }), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToGregorianLikeShortYear: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortYearFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() }), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToGregorianLikeLongYearMonth: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongYearMonthFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() }), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToGregorianLikeShortYearMonth: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortYearMonthFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() }), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToGregorianLikeLongMonth: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, 'MMMM', getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() }), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToGregorianLikeShortMonth: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, 'MMM', getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() }), utcOffset, currentUtcOffset),
|
||||
formatDateTimeToGregorianLikeLongYear: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedLongYearFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() })),
|
||||
formatDateTimeToGregorianLikeShortYear: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedShortYearFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() })),
|
||||
formatDateTimeToGregorianLikeLongYearMonth: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedLongYearMonthFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() })),
|
||||
formatDateTimeToGregorianLikeShortYearMonth: (dateTime: DateTime) => formatDateTime(dateTime, getLocalizedShortYearMonthFormat(), getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() })),
|
||||
formatDateTimeToGregorianLikeLongMonth: (dateTime: DateTime) => formatDateTime(dateTime, 'MMMM', getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() })),
|
||||
formatDateTimeToGregorianLikeShortMonth: (dateTime: DateTime) => formatDateTime(dateTime, 'MMM', getDateTimeFormatOptions({ calendarType: getGregorianLikeCalendarType() })),
|
||||
formatGregorianTextualMonthDayToGregorianLikeLongMonthDay,
|
||||
formatUnixTimeToGregorianLikeYearQuarter,
|
||||
formatDateTimeToGregorianLikeYearQuarter,
|
||||
formatYearQuarterToGregorianLikeYearQuarter,
|
||||
formatUnixTimeToGregorianLikeFiscalYear,
|
||||
formatDateTimeToGregorianLikeFiscalYear: (dateTime: DateTime) => formatUnixTimeToGregorianLikeFiscalYear(dateTime.getUnixTime()),
|
||||
formatGregorianYearToGregorianLikeFiscalYear,
|
||||
formatFiscalYearStartToGregorianLikeLongMonth,
|
||||
// format date time (Gregorian calendar) functions
|
||||
formatUnixTimeToGregorianDefaultDateTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, KnownDateTimeFormat.DefaultDateTime.format, getDateTimeFormatOptions({ numeralSystem: NumeralSystem.WesternArabicNumerals, calendarType: CalendarType.Gregorian }), utcOffset, currentUtcOffset),
|
||||
formatDateTimeToGregorianDefaultDateTime: (dateTime: DateTime) => formatDateTime(dateTime, KnownDateTimeFormat.DefaultDateTime.format, getDateTimeFormatOptions({ numeralSystem: NumeralSystem.WesternArabicNumerals, calendarType: CalendarType.Gregorian })),
|
||||
// other format date time functions
|
||||
formatDateRange,
|
||||
getTimezoneDifferenceDisplayText,
|
||||
|
||||
+18
-19
@@ -226,11 +226,11 @@ export class Transaction implements TransactionInfoResponse {
|
||||
this._displayDayOfWeek = displayDayOfWeek;
|
||||
}
|
||||
|
||||
public toCreateRequest(clientSessionId: string, actualTime?: number): TransactionCreateRequest {
|
||||
public toCreateRequest(clientSessionId: string): TransactionCreateRequest {
|
||||
return {
|
||||
type: this.type,
|
||||
categoryId: this.getCategoryId(),
|
||||
time: actualTime ? actualTime : this.time,
|
||||
time: this.time,
|
||||
utcOffset: this.utcOffset,
|
||||
sourceAccountId: this.sourceAccountId,
|
||||
destinationAccountId: this.type === TransactionType.Transfer ? this.destinationAccountId : '0',
|
||||
@@ -245,7 +245,7 @@ export class Transaction implements TransactionInfoResponse {
|
||||
};
|
||||
}
|
||||
|
||||
public toModifyRequest(actualTime?: number): TransactionModifyRequest {
|
||||
public toModifyRequest(): TransactionModifyRequest {
|
||||
let categoryId = this.getCategoryId();
|
||||
|
||||
if (this.type === TransactionType.ModifyBalance) {
|
||||
@@ -255,7 +255,7 @@ export class Transaction implements TransactionInfoResponse {
|
||||
return {
|
||||
id: this.id,
|
||||
categoryId: categoryId,
|
||||
time: actualTime ? actualTime : this.time,
|
||||
time: this.time,
|
||||
utcOffset: this.utcOffset,
|
||||
sourceAccountId: this.sourceAccountId,
|
||||
destinationAccountId: this.type === TransactionType.Transfer ? this.destinationAccountId : '0',
|
||||
@@ -677,20 +677,20 @@ export const ALL_TRANSACTION_AMOUNTS_REQUEST_TYPE = [
|
||||
|
||||
export type TransactionAmountsRequestType = typeof ALL_TRANSACTION_AMOUNTS_REQUEST_TYPE[number];
|
||||
|
||||
export const LATEST_12MONTHS_TRANSACTION_AMOUNTS_REQUEST_TYPES: PartialRecord<TransactionAmountsRequestType, number> = {
|
||||
'monthBeforeLast10Months': 11,
|
||||
'monthBeforeLast9Months': 10,
|
||||
'monthBeforeLast8Months': 9,
|
||||
'monthBeforeLast7Months': 8,
|
||||
'monthBeforeLast6Months': 7,
|
||||
'monthBeforeLast5Months': 6,
|
||||
'monthBeforeLast4Months': 5,
|
||||
'monthBeforeLast3Months': 4,
|
||||
'monthBeforeLast2Months': 3,
|
||||
'monthBeforeLastMonth': 2,
|
||||
'lastMonth': 1,
|
||||
'thisMonth': 0
|
||||
};
|
||||
export const LATEST_12MONTHS_TRANSACTION_AMOUNTS_REQUEST_TYPES: TransactionAmountsRequestType[] = [
|
||||
'monthBeforeLast10Months',
|
||||
'monthBeforeLast9Months',
|
||||
'monthBeforeLast8Months',
|
||||
'monthBeforeLast7Months',
|
||||
'monthBeforeLast6Months',
|
||||
'monthBeforeLast5Months',
|
||||
'monthBeforeLast4Months',
|
||||
'monthBeforeLast3Months',
|
||||
'monthBeforeLast2Months',
|
||||
'monthBeforeLastMonth',
|
||||
'lastMonth',
|
||||
'thisMonth'
|
||||
];
|
||||
|
||||
export interface TransactionAmountsRequestParams extends PartialRecord<TransactionAmountsRequestType, StartEndTime> {
|
||||
readonly useTransactionTimezone: boolean;
|
||||
@@ -1009,7 +1009,6 @@ export interface TransactionOverviewResponseItem {
|
||||
|
||||
export interface TransactionMonthlyIncomeAndExpenseData {
|
||||
readonly monthStartTime: number;
|
||||
readonly monthsBeforeCurrentMonth: number;
|
||||
readonly incomeAmount: number;
|
||||
readonly expenseAmount: number;
|
||||
readonly incompleteIncomeAmount: boolean;
|
||||
|
||||
@@ -53,12 +53,7 @@ import {
|
||||
splitItemsToMap,
|
||||
countSplitItems
|
||||
} from '@/lib/common.ts';
|
||||
import {
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getActualUnixTimeForStore,
|
||||
parseDateTimeFromUnixTime
|
||||
} from '@/lib/datetime.ts';
|
||||
import { parseDateTimeFromUnixTimeWithTimezoneOffset } from '@/lib/datetime.ts';
|
||||
import { getAmountWithDecimalNumberCount } from '@/lib/numeral.ts';
|
||||
import { getCurrencyFraction } from '@/lib/currency.ts';
|
||||
import { getFirstVisibleCategoryId } from '@/lib/category.ts';
|
||||
@@ -185,14 +180,13 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
}
|
||||
|
||||
if (transactionPageWrapper.items && transactionPageWrapper.items.length) {
|
||||
const currentUtcOffset = getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone);
|
||||
let currentMonthListIndex = -1;
|
||||
let currentMonthList: TransactionMonthList | null = null;
|
||||
|
||||
for (const [item, index] of itemAndIndex(transactionPageWrapper.items)) {
|
||||
fillTransactionObject(item, currentUtcOffset);
|
||||
fillTransactionObject(item);
|
||||
|
||||
const transactionTime = parseDateTimeFromUnixTime(item.time, item.utcOffset, currentUtcOffset);
|
||||
const transactionTime = parseDateTimeFromUnixTimeWithTimezoneOffset(item.time, item.utcOffset);
|
||||
const transactionYear = transactionTime.getGregorianCalendarYear();
|
||||
const transactionMonth = transactionTime.getGregorianCalendarMonth();
|
||||
const transactionYearDashMonth = transactionTime.getGregorianCalendarYearDashMonth();
|
||||
@@ -264,8 +258,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
}
|
||||
|
||||
function updateTransactionInTransactionList({ currentTransaction, defaultCurrency }: { currentTransaction: Transaction, defaultCurrency: string }): void {
|
||||
const currentUtcOffset = getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone);
|
||||
const transactionTime = parseDateTimeFromUnixTime(currentTransaction.time, currentTransaction.utcOffset, currentUtcOffset);
|
||||
const transactionTime = parseDateTimeFromUnixTimeWithTimezoneOffset(currentTransaction.time, currentTransaction.utcOffset);
|
||||
const transactionYear = transactionTime.getGregorianCalendarYear();
|
||||
const transactionMonth = transactionTime.getGregorianCalendarMonth();
|
||||
|
||||
@@ -276,7 +269,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
|
||||
for (const [transaction, transactionIndex] of itemAndIndex(transactionMonthList.items)) {
|
||||
if (transaction.id === currentTransaction.id) {
|
||||
fillTransactionObject(currentTransaction, currentUtcOffset);
|
||||
fillTransactionObject(currentTransaction);
|
||||
|
||||
if (transactionYear !== transactionMonthList.year ||
|
||||
transactionMonth !== transactionMonthList.month ||
|
||||
@@ -445,12 +438,12 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function fillTransactionObject(transaction: Transaction, currentUtcOffset: number): void {
|
||||
function fillTransactionObject(transaction: Transaction): void {
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
const transactionTime = parseDateTimeFromUnixTime(transaction.time, transaction.utcOffset, currentUtcOffset);
|
||||
const transactionTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
transaction.setDisplayDate(transactionTime.getGregorianCalendarYearDashMonthDashDay(), transactionTime.getGregorianCalendarDay(), transactionTime.getWeekDay());
|
||||
|
||||
if (transaction.sourceAccountId) {
|
||||
@@ -1026,7 +1019,6 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
|
||||
function saveTransaction({ transaction, defaultCurrency, isEdit, clientSessionId }: { transaction: Transaction, defaultCurrency: string, isEdit: boolean, clientSessionId: string }): Promise<Transaction> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const actualTime = getActualUnixTimeForStore(transaction.time, transaction.utcOffset, getBrowserTimezoneOffsetMinutes());
|
||||
let promise: ApiResponsePromise<TransactionInfoResponse>;
|
||||
|
||||
if (transaction.type !== TransactionType.Expense &&
|
||||
@@ -1041,9 +1033,9 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
||||
}
|
||||
|
||||
if (!isEdit) {
|
||||
promise = services.addTransaction(transaction.toCreateRequest(clientSessionId, actualTime));
|
||||
promise = services.addTransaction(transaction.toCreateRequest(clientSessionId));
|
||||
} else {
|
||||
promise = services.modifyTransaction(transaction.toModifyRequest(actualTime));
|
||||
promise = services.modifyTransaction(transaction.toModifyRequest());
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { VersionInfo } from '@/core/version.ts';
|
||||
|
||||
import type { LatestExchangeRateResponse } from '@/models/exchange_rate.ts';
|
||||
|
||||
import { parseDateTimeFromUnixTime } from '@/lib/datetime.ts';
|
||||
import { getMapProvider } from '@/lib/server_settings.ts';
|
||||
import { getMapWebsite } from '@/lib/map/index.ts';
|
||||
import { getLicense, getThirdPartyLicenses } from '@/lib/licenses.ts';
|
||||
@@ -16,7 +17,7 @@ import { formatDisplayVersion, getClientDisplayVersion, getClientBuildTime } fro
|
||||
import { clearBrowserCaches } from '@/lib/ui/common.ts';
|
||||
|
||||
export function useAboutPageBase() {
|
||||
const { tt, formatUnixTimeToLongDateTime } = useI18n();
|
||||
const { tt, formatDateTimeToLongDateTime } = useI18n();
|
||||
|
||||
const systemsStore = useSystemsStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
@@ -41,7 +42,8 @@ export function useAboutPageBase() {
|
||||
return time;
|
||||
}
|
||||
|
||||
return formatUnixTimeToLongDateTime(parseInt(time));
|
||||
const buildDateTime = parseDateTimeFromUnixTime(parseInt(time));
|
||||
return formatDateTimeToLongDateTime(buildDateTime);
|
||||
});
|
||||
|
||||
const exchangeRatesData = computed<LatestExchangeRateResponse | undefined>(() => exchangeRatesStore.latestExchangeRates.data);
|
||||
|
||||
@@ -12,9 +12,10 @@ import type {
|
||||
} from '@/models/exchange_rate.ts';
|
||||
|
||||
import { getExchangedAmountByRate } from '@/lib/numeral.ts';
|
||||
import { parseDateTimeFromUnixTime } from '@/lib/datetime.ts';
|
||||
|
||||
export function useExchangeRatesPageBase() {
|
||||
const { getAllDisplayExchangeRates, formatUnixTimeToLongDate, parseAmountFromWesternArabicNumerals } = useI18n();
|
||||
const { getAllDisplayExchangeRates, formatDateTimeToLongDate, parseAmountFromWesternArabicNumerals } = useI18n();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
@@ -27,8 +28,12 @@ export function useExchangeRatesPageBase() {
|
||||
const isUserCustomExchangeRates = computed<boolean>(() => exchangeRatesStore.isUserCustomExchangeRates);
|
||||
|
||||
const exchangeRatesDataUpdateTime = computed<string>(() => {
|
||||
const exchangeRatesLastUpdateTime = exchangeRatesStore.exchangeRatesLastUpdateTime;
|
||||
return exchangeRatesLastUpdateTime ? formatUnixTimeToLongDate(exchangeRatesLastUpdateTime) : '';
|
||||
if (!exchangeRatesStore.exchangeRatesLastUpdateTime) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const exchangeRatesLastUpdateTime = parseDateTimeFromUnixTime(exchangeRatesStore.exchangeRatesLastUpdateTime);
|
||||
return formatDateTimeToLongDate(exchangeRatesLastUpdateTime);
|
||||
});
|
||||
|
||||
const availableExchangeRates = computed<LocalizedLatestExchangeRate[]>(() => {
|
||||
|
||||
@@ -17,12 +17,14 @@ import type {
|
||||
TransactionOverviewResponseItem
|
||||
} from '@/models/transaction.ts';
|
||||
|
||||
import { parseDateTimeFromUnixTime } from '@/lib/datetime.ts';
|
||||
|
||||
export function useHomePageBase() {
|
||||
const {
|
||||
formatUnixTimeToLongDate,
|
||||
formatUnixTimeToLongMonthDay,
|
||||
formatUnixTimeToGregorianLikeLongYear,
|
||||
formatUnixTimeToGregorianLikeLongMonth,
|
||||
formatDateTimeToLongDate,
|
||||
formatDateTimeToLongMonthDay,
|
||||
formatDateTimeToGregorianLikeLongYear,
|
||||
formatDateTimeToGregorianLikeLongMonth,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
@@ -57,19 +59,19 @@ export function useHomePageBase() {
|
||||
const displayDateRange = computed<TransactionOverviewDisplayTime>(() => {
|
||||
return {
|
||||
today: {
|
||||
displayTime: formatUnixTimeToLongDate(overviewStore.transactionDataRange.today.startTime),
|
||||
displayTime: formatDateTimeToLongDate(parseDateTimeFromUnixTime(overviewStore.transactionDataRange.today.startTime)),
|
||||
},
|
||||
thisWeek: {
|
||||
startTime: formatUnixTimeToLongMonthDay(overviewStore.transactionDataRange.thisWeek.startTime),
|
||||
endTime: formatUnixTimeToLongMonthDay(overviewStore.transactionDataRange.thisWeek.endTime)
|
||||
startTime: formatDateTimeToLongMonthDay(parseDateTimeFromUnixTime(overviewStore.transactionDataRange.thisWeek.startTime)),
|
||||
endTime: formatDateTimeToLongMonthDay(parseDateTimeFromUnixTime(overviewStore.transactionDataRange.thisWeek.endTime))
|
||||
},
|
||||
thisMonth: {
|
||||
displayTime: formatUnixTimeToGregorianLikeLongMonth(overviewStore.transactionDataRange.thisMonth.startTime),
|
||||
startTime: formatUnixTimeToLongMonthDay(overviewStore.transactionDataRange.thisMonth.startTime),
|
||||
endTime: formatUnixTimeToLongMonthDay(overviewStore.transactionDataRange.thisMonth.endTime)
|
||||
displayTime: formatDateTimeToGregorianLikeLongMonth(parseDateTimeFromUnixTime(overviewStore.transactionDataRange.thisMonth.startTime)),
|
||||
startTime: formatDateTimeToLongMonthDay(parseDateTimeFromUnixTime(overviewStore.transactionDataRange.thisMonth.startTime)),
|
||||
endTime: formatDateTimeToLongMonthDay(parseDateTimeFromUnixTime(overviewStore.transactionDataRange.thisMonth.endTime))
|
||||
},
|
||||
thisYear: {
|
||||
displayTime: formatUnixTimeToGregorianLikeLongYear(overviewStore.transactionDataRange.thisYear.startTime)
|
||||
displayTime: formatDateTimeToGregorianLikeLongYear(parseDateTimeFromUnixTime(overviewStore.transactionDataRange.thisYear.startTime))
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -9,7 +9,13 @@ import { AccountCategory, AccountType } from '@/core/account.ts';
|
||||
import type { LocalizedAccountCategory } from '@/core/account.ts';
|
||||
import { Account } from '@/models/account.ts';
|
||||
|
||||
import { getCurrentUnixTime } from '@/lib/datetime.ts';
|
||||
import { isDefined } from '@/lib/common.ts';
|
||||
import {
|
||||
getTimezoneOffsetMinutes,
|
||||
getSameDateTimeWithCurrentTimezone,
|
||||
parseDateTimeFromUnixTimeWithBrowserTimezone,
|
||||
getCurrentUnixTime
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
export interface DayAndDisplayName {
|
||||
readonly day: number;
|
||||
@@ -25,7 +31,7 @@ export function useAccountEditPageBase() {
|
||||
const clientSessionId = ref<string>('');
|
||||
const loading = ref<boolean>(false);
|
||||
const submitting = ref<boolean>(false);
|
||||
const account = ref<Account>(Account.createNewAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTime()));
|
||||
const account = ref<Account>(Account.createNewAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTimeForNewAccount()));
|
||||
const subAccounts = ref<Account[]>([]);
|
||||
|
||||
const title = computed<string>(() => {
|
||||
@@ -89,6 +95,18 @@ export function useAccountEditPageBase() {
|
||||
|
||||
const isAccountSupportCreditCardStatementDate = computed<boolean>(() => account.value && account.value.category === AccountCategory.CreditCard.type);
|
||||
|
||||
function getCurrentUnixTimeForNewAccount(): number {
|
||||
return getSameDateTimeWithCurrentTimezone(parseDateTimeFromUnixTimeWithBrowserTimezone(getCurrentUnixTime())).getUnixTime();
|
||||
}
|
||||
|
||||
function getDefaultTimezoneOffsetMinutes(account: Account): number {
|
||||
if (!account.balanceTime) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return getTimezoneOffsetMinutes(account.balanceTime);
|
||||
}
|
||||
|
||||
function getAccountCreditCardStatementDate(statementDate?: number): string | null {
|
||||
for (const item of allAvailableMonthDays.value) {
|
||||
if (item.day === statementDate) {
|
||||
@@ -99,6 +117,23 @@ export function useAccountEditPageBase() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function updateAccountBalanceTime(account: Account, balanceTime: number): void {
|
||||
if (!isDefined(account.balanceTime)) {
|
||||
account.balanceTime = balanceTime;
|
||||
return;
|
||||
}
|
||||
|
||||
const oldUtcOffset = getTimezoneOffsetMinutes(account.balanceTime);
|
||||
const newUtcOffset = getTimezoneOffsetMinutes(balanceTime);
|
||||
|
||||
if (oldUtcOffset === newUtcOffset) {
|
||||
account.balanceTime = balanceTime;
|
||||
return;
|
||||
}
|
||||
|
||||
account.balanceTime = balanceTime - (newUtcOffset - oldUtcOffset) * 60;
|
||||
}
|
||||
|
||||
function getInputEmptyProblemMessage(account: Account, isSubAccount: boolean): string | null {
|
||||
if (!isSubAccount && !account.category) {
|
||||
return 'Account category cannot be blank';
|
||||
@@ -122,7 +157,7 @@ export function useAccountEditPageBase() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const subAccount = account.value.createNewSubAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTime());
|
||||
const subAccount = account.value.createNewSubAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTimeForNewAccount());
|
||||
subAccounts.value.push(subAccount);
|
||||
return true;
|
||||
}
|
||||
@@ -133,7 +168,7 @@ export function useAccountEditPageBase() {
|
||||
|
||||
if (newAccount.subAccounts && newAccount.subAccounts.length > 0) {
|
||||
for (const oldSubAccount of newAccount.subAccounts) {
|
||||
const subAccount: Account = account.value.createNewSubAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTime());
|
||||
const subAccount: Account = account.value.createNewSubAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTimeForNewAccount());
|
||||
subAccount.fillFrom(oldSubAccount);
|
||||
|
||||
subAccounts.value.push(subAccount);
|
||||
@@ -163,7 +198,10 @@ export function useAccountEditPageBase() {
|
||||
allAvailableMonthDays,
|
||||
isAccountSupportCreditCardStatementDate,
|
||||
// functions
|
||||
getCurrentUnixTimeForNewAccount,
|
||||
getDefaultTimezoneOffsetMinutes,
|
||||
getAccountCreditCardStatementDate,
|
||||
updateAccountBalanceTime,
|
||||
isNewAccount,
|
||||
addSubAccount,
|
||||
setAccount
|
||||
|
||||
@@ -2,7 +2,6 @@ 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';
|
||||
@@ -25,7 +24,8 @@ import { replaceAll } from '@/lib/common.ts';
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffsetMinutes,
|
||||
parseDateTimeFromUnixTime
|
||||
parseDateTimeFromUnixTime,
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
export function useReconciliationStatementPageBase() {
|
||||
@@ -33,15 +33,14 @@ export function useReconciliationStatementPageBase() {
|
||||
tt,
|
||||
getAllAccountBalanceTrendChartTypes,
|
||||
getAllStatisticsDateAggregationTypesWithShortName,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatUnixTimeToLongDate,
|
||||
formatUnixTimeToShortTime,
|
||||
formatUnixTimeToGregorianDefaultDateTime,
|
||||
formatDateTimeToLongDateTime,
|
||||
formatDateTimeToLongDate,
|
||||
formatDateTimeToShortTime,
|
||||
formatDateTimeToGregorianDefaultDateTime,
|
||||
formatAmountToWesternArabicNumeralsWithoutDigitGrouping,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const userStore = useUserStore();
|
||||
const accountsStore = useAccountsStore();
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
@@ -53,7 +52,6 @@ export function useReconciliationStatementPageBase() {
|
||||
|
||||
const firstDayOfWeek = computed<WeekDayValue>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const fiscalYearStart = computed<number>(() => userStore.currentUserFiscalYearStart);
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||
|
||||
const allChartTypes = computed<TypeAndDisplayName[]>(() => getAllAccountBalanceTrendChartTypes());
|
||||
@@ -79,11 +77,13 @@ export function useReconciliationStatementPageBase() {
|
||||
const allCategoriesMap = computed<Record<string, TransactionCategory>>(() => transactionCategoriesStore.allTransactionCategoriesMap);
|
||||
|
||||
const displayStartDateTime = computed<string>(() => {
|
||||
return formatUnixTimeToLongDateTime(startTime.value);
|
||||
const dateTime = parseDateTimeFromUnixTime(startTime.value);
|
||||
return formatDateTimeToLongDateTime(dateTime);
|
||||
});
|
||||
|
||||
const displayEndDateTime = computed<string>(() => {
|
||||
return formatUnixTimeToLongDateTime(endTime.value);
|
||||
const dateTime = parseDateTimeFromUnixTime(endTime.value);
|
||||
return formatDateTimeToLongDateTime(dateTime);
|
||||
});
|
||||
|
||||
const displayTotalInflows = computed<string>(() => {
|
||||
@@ -160,15 +160,22 @@ export function useReconciliationStatementPageBase() {
|
||||
}
|
||||
|
||||
function getDisplayDateTime(transaction: TransactionReconciliationStatementResponseItemWithInfo): string {
|
||||
return formatUnixTimeToLongDateTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
return formatDateTimeToLongDateTime(dateTime);
|
||||
}
|
||||
|
||||
function getDisplayDate(transaction: TransactionReconciliationStatementResponseItemWithInfo): string {
|
||||
return formatUnixTimeToLongDate(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
return formatDateTimeToLongDate(dateTime);
|
||||
}
|
||||
|
||||
function getDisplayTime(transaction: TransactionReconciliationStatementResponseItemWithInfo): string {
|
||||
return formatUnixTimeToShortTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
return formatDateTimeToShortTime(dateTime);
|
||||
}
|
||||
|
||||
function isSameAsDefaultTimezoneOffsetMinutes(transaction: TransactionReconciliationStatementResponseItemWithInfo): boolean {
|
||||
return transaction.utcOffset === getTimezoneOffsetMinutes(transaction.time);
|
||||
}
|
||||
|
||||
function getDisplayTimezone(transaction: TransactionReconciliationStatementResponseItemWithInfo): string {
|
||||
@@ -227,7 +234,7 @@ export function useReconciliationStatementPageBase() {
|
||||
|
||||
const transactions = reconciliationStatements.value?.transactions ?? [];
|
||||
const rows = transactions.map(transaction => {
|
||||
const transactionTime = parseDateTimeFromUnixTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value).getUnixTime();
|
||||
const transactionTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
const type = getDisplayTransactionType(transaction);
|
||||
let categoryName = transaction.categoryName;
|
||||
let displayAmount = formatAmountToWesternArabicNumeralsWithoutDigitGrouping(transaction.sourceAmount);
|
||||
@@ -260,7 +267,7 @@ export function useReconciliationStatementPageBase() {
|
||||
}
|
||||
|
||||
return [
|
||||
formatUnixTimeToGregorianDefaultDateTime(transactionTime),
|
||||
formatDateTimeToGregorianDefaultDateTime(transactionTime),
|
||||
type,
|
||||
categoryName,
|
||||
displayAmount,
|
||||
@@ -282,7 +289,6 @@ export function useReconciliationStatementPageBase() {
|
||||
// computed states
|
||||
firstDayOfWeek,
|
||||
fiscalYearStart,
|
||||
currentTimezoneOffsetMinutes,
|
||||
defaultCurrency,
|
||||
allChartTypes,
|
||||
allDateAggregationTypes,
|
||||
@@ -303,6 +309,7 @@ export function useReconciliationStatementPageBase() {
|
||||
getDisplayDateTime,
|
||||
getDisplayDate,
|
||||
getDisplayTime,
|
||||
isSameAsDefaultTimezoneOffsetMinutes,
|
||||
getDisplayTimezone,
|
||||
getDisplaySourceAmount,
|
||||
getDisplayDestinationAmount,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { CategoryType } from '@/core/category.ts';
|
||||
import type { Account } from '@/models/account.ts';
|
||||
|
||||
import { isObjectEmpty } from '@/lib/common.ts';
|
||||
import { getCurrentUnixTime } from '@/lib/datetime.ts';
|
||||
|
||||
export function useAppSettingPageBase() {
|
||||
const { tt, getAllTimezones, getAllTimezoneTypesUsedForStatistics, getAllCurrencySortingTypes, setTimeZone } = useI18n();
|
||||
@@ -37,7 +38,7 @@ export function useAppSettingPageBase() {
|
||||
];
|
||||
});
|
||||
|
||||
const allTimezones = computed<LocalizedTimezoneInfo[]>(() => getAllTimezones(true));
|
||||
const allTimezones = computed<LocalizedTimezoneInfo[]>(() => getAllTimezones(getCurrentUnixTime(), true));
|
||||
const allTimezoneTypesUsedForStatistics = computed<TypeAndDisplayName[]>(() => getAllTimezoneTypesUsedForStatistics());
|
||||
const allCurrencySortingTypes = computed<TypeAndDisplayName[]>(() => getAllCurrencySortingTypes());
|
||||
|
||||
|
||||
@@ -28,7 +28,11 @@ import type {
|
||||
} from '@/models/transaction.ts';
|
||||
|
||||
import { limitText, findNameByType, findDisplayNameByType } from '@/lib/common.ts';
|
||||
import { getYearMonthFirstUnixTime, getYearMonthLastUnixTime } from '@/lib/datetime.ts';
|
||||
import {
|
||||
parseDateTimeFromUnixTime,
|
||||
getYearMonthFirstUnixTime,
|
||||
getYearMonthLastUnixTime
|
||||
} from '@/lib/datetime.ts';
|
||||
import { getDisplayColor, getCategoryDisplayColor, getAccountDisplayColor } from '@/lib/color.ts';
|
||||
|
||||
export function useStatisticsTransactionPageBase() {
|
||||
@@ -37,8 +41,8 @@ export function useStatisticsTransactionPageBase() {
|
||||
getAllDateRanges,
|
||||
getAllStatisticsSortingTypes,
|
||||
getAllStatisticsDateAggregationTypes,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatUnixTimeToGregorianLikeLongYearMonth,
|
||||
formatDateTimeToLongDateTime,
|
||||
formatDateTimeToGregorianLikeLongYearMonth,
|
||||
formatDateRange,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
@@ -88,11 +92,11 @@ export function useStatisticsTransactionPageBase() {
|
||||
|
||||
const queryStartTime = computed<string>(() => {
|
||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||
return formatUnixTimeToLongDateTime(query.value.categoricalChartStartTime);
|
||||
return formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.categoricalChartStartTime));
|
||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||
return formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth));
|
||||
return formatDateTimeToGregorianLikeLongYearMonth(parseDateTimeFromUnixTime(getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth)));
|
||||
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||
return formatUnixTimeToLongDateTime(query.value.assetTrendsChartStartTime);
|
||||
return formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.assetTrendsChartStartTime));
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
@@ -100,11 +104,11 @@ export function useStatisticsTransactionPageBase() {
|
||||
|
||||
const queryEndTime = computed<string>(() => {
|
||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||
return formatUnixTimeToLongDateTime(query.value.categoricalChartEndTime);
|
||||
return formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.categoricalChartEndTime));
|
||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||
return formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
|
||||
return formatDateTimeToGregorianLikeLongYearMonth(parseDateTimeFromUnixTime(getYearMonthLastUnixTime(query.value.trendChartEndYearMonth)));
|
||||
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||
return formatUnixTimeToLongDateTime(query.value.assetTrendsChartEndTime);
|
||||
return formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.assetTrendsChartEndTime));
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ import {
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffsetMinutes,
|
||||
getSameDateTimeWithCurrentTimezone,
|
||||
parseDateTimeFromUnixTimeWithBrowserTimezone,
|
||||
getCurrentUnixTime
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
@@ -92,14 +94,14 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo
|
||||
const transaction = ref<Transaction | TransactionTemplate>(createNewTransactionModel(transactionDefaultType));
|
||||
|
||||
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(transaction.value.time));
|
||||
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
|
||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||
const defaultAccountId = computed<string>(() => userStore.currentUserDefaultAccountId);
|
||||
const firstDayOfWeek = computed<WeekDayValue>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const coordinateDisplayType = computed<number>(() => userStore.currentUserCoordinateDisplayType);
|
||||
|
||||
const allTimezones = computed<LocalizedTimezoneInfo[]>(() => getAllTimezones(true));
|
||||
const allTimezones = computed<LocalizedTimezoneInfo[]>(() => getAllTimezones(transaction.value.time, true));
|
||||
const allAccounts = computed<Account[]>(() => accountsStore.allPlainAccounts);
|
||||
const allVisibleAccounts = computed<Account[]>(() => accountsStore.allVisiblePlainAccounts);
|
||||
const allAccountsMap = computed<Record<string, Account>>(() => accountsStore.allAccountsMap);
|
||||
@@ -274,7 +276,7 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo
|
||||
});
|
||||
|
||||
const transactionTimezoneTimeDifference = computed<string>(() => {
|
||||
return getTimezoneDifferenceDisplayText(transaction.value.utcOffset);
|
||||
return getTimezoneDifferenceDisplayText(transaction.value.time, transaction.value.utcOffset);
|
||||
});
|
||||
|
||||
const geoLocationStatusInfo = computed<string>(() => {
|
||||
@@ -331,8 +333,12 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo
|
||||
return !!inputEmptyProblemMessage.value;
|
||||
});
|
||||
|
||||
function getCurrentUnixTimeForNewTransaction(): number {
|
||||
return getSameDateTimeWithCurrentTimezone(parseDateTimeFromUnixTimeWithBrowserTimezone(getCurrentUnixTime())).getUnixTime();
|
||||
}
|
||||
|
||||
function createNewTransactionModel(transactionType?: number): Transaction | TransactionTemplate {
|
||||
const now: number = getCurrentUnixTime();
|
||||
const now: number = getCurrentUnixTimeForNewTransaction();
|
||||
const currentTimezone: string = settingsStore.appSettings.timeZone;
|
||||
|
||||
let defaultType: TransactionType = TransactionType.Expense;
|
||||
@@ -343,7 +349,7 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo
|
||||
defaultType = TransactionType.Transfer;
|
||||
}
|
||||
|
||||
let newTransaction: Transaction | TransactionTemplate = Transaction.createNewTransaction(defaultType, now, currentTimezone, getTimezoneOffsetMinutes(currentTimezone));
|
||||
let newTransaction: Transaction | TransactionTemplate = Transaction.createNewTransaction(defaultType, now, currentTimezone, getTimezoneOffsetMinutes(now, currentTimezone));
|
||||
|
||||
if (type === TransactionEditPageType.Template) {
|
||||
newTransaction = TransactionTemplate.createNewTransactionTemplate(newTransaction);
|
||||
@@ -352,6 +358,25 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo
|
||||
return newTransaction;
|
||||
}
|
||||
|
||||
function updateTransactionTime(newTime: number): void {
|
||||
transaction.value.time = newTime;
|
||||
updateTransactionTimezone(transaction.value.timeZone ?? '');
|
||||
}
|
||||
|
||||
function updateTransactionTimezone(timezoneName: string): void {
|
||||
const oldUtcOffset = transaction.value.utcOffset;
|
||||
|
||||
for (const timezone of allTimezones.value) {
|
||||
if (timezone.name === timezoneName) {
|
||||
transaction.value.timeZone = timezone.name;
|
||||
transaction.value.utcOffset = timezone.utcOffsetMinutes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
transaction.value.time = transaction.value.time - (transaction.value.utcOffset - oldUtcOffset) * 60;
|
||||
}
|
||||
|
||||
function swapTransactionData(swapAccount: boolean, swapAmount: boolean): void {
|
||||
if (swapAccount) {
|
||||
const oldSourceAccountId = transaction.value.sourceAccountId;
|
||||
@@ -396,15 +421,6 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => transaction.value.timeZone, (newValue) => {
|
||||
for (const timezone of allTimezones.value) {
|
||||
if (timezone.name === newValue) {
|
||||
transaction.value.utcOffset = timezone.utcOffsetMinutes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
// constants
|
||||
isSupportGeoLocation,
|
||||
@@ -460,6 +476,8 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo
|
||||
inputIsEmpty,
|
||||
// functions
|
||||
createNewTransactionModel,
|
||||
updateTransactionTime,
|
||||
updateTransactionTimezone,
|
||||
swapTransactionData,
|
||||
getDisplayAmount,
|
||||
getTransactionPictureUrl
|
||||
|
||||
@@ -23,13 +23,12 @@ import { type Transaction, TransactionTagFilter } from '@/models/transaction.ts'
|
||||
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffset,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getSameDateTimeWithBrowserTimezone,
|
||||
getCurrentDateTime,
|
||||
parseDateTimeFromUnixTime,
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset,
|
||||
getYearMonthFirstUnixTime,
|
||||
isDateRangeMatchOneMonth
|
||||
} from '@/lib/datetime.ts';
|
||||
@@ -76,10 +75,10 @@ export function useTransactionListPageBase() {
|
||||
tt,
|
||||
getAllDateRanges,
|
||||
getCurrentNumeralSystemType,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatUnixTimeToLongDate,
|
||||
formatUnixTimeToShortTime,
|
||||
formatUnixTimeToGregorianLikeLongYearMonth,
|
||||
formatDateTimeToLongDateTime,
|
||||
formatDateTimeToLongDate,
|
||||
formatDateTimeToShortTime,
|
||||
formatDateTimeToGregorianLikeLongYearMonth,
|
||||
formatDateRange,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
@@ -98,7 +97,6 @@ export function useTransactionListPageBase() {
|
||||
const currentCalendarDate = ref<TextualYearMonthDay | ''>('');
|
||||
|
||||
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||
const firstDayOfWeek = computed<WeekDayValue>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const fiscalYearStart = computed<number>(() => userStore.currentUserFiscalYearStart);
|
||||
const defaultCurrency = computed<string>(() => getUnifiedSelectedAccountsCurrencyOrDefaultCurrency(allAccountsMap.value, queryAllFilterAccountIds.value, userStore.currentUserDefaultCurrency));
|
||||
@@ -161,8 +159,8 @@ export function useTransactionListPageBase() {
|
||||
|
||||
return formatDateRange(query.value.dateType, query.value.minTime, query.value.maxTime);
|
||||
});
|
||||
const queryMinTime = computed<string>(() => formatUnixTimeToLongDateTime(query.value.minTime));
|
||||
const queryMaxTime = computed<string>(() => formatUnixTimeToLongDateTime(query.value.maxTime));
|
||||
const queryMinTime = computed<string>(() => formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.minTime)));
|
||||
const queryMaxTime = computed<string>(() => formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.maxTime)));
|
||||
const queryMonthlyData = computed<boolean>(() => isDateRangeMatchOneMonth(query.value.minTime, query.value.maxTime));
|
||||
const queryMonth = computed<Year0BasedMonth>(() => {
|
||||
if (!query.value.minTime || !query.value.maxTime) {
|
||||
@@ -235,8 +233,8 @@ export function useTransactionListPageBase() {
|
||||
return displayAmount.join(' ~ ');
|
||||
});
|
||||
|
||||
const transactionCalendarMinDate = computed<Date>(() => getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(query.value.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())));
|
||||
const transactionCalendarMaxDate = computed<Date>(() => getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(query.value.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())));
|
||||
const transactionCalendarMinDate = computed<Date>(() => getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(parseDateTimeFromUnixTime(query.value.minTime)).getUnixTime()));
|
||||
const transactionCalendarMaxDate = computed<Date>(() => getLocalDatetimeFromUnixTime(getSameDateTimeWithBrowserTimezone(parseDateTimeFromUnixTime(query.value.maxTime)).getUnixTime()));
|
||||
|
||||
const currentMonthTransactionData = computed<TransactionMonthList | null>(() => {
|
||||
const allTransactions = transactionsStore.transactions;
|
||||
@@ -284,6 +282,10 @@ export function useTransactionListPageBase() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSameAsDefaultTimezoneOffsetMinutes(transaction: Transaction): boolean {
|
||||
return transaction.utcOffset === getTimezoneOffsetMinutes(transaction.time);
|
||||
}
|
||||
|
||||
function formatAmount(amount: number, hideAmount: boolean, currencyCode: string): string {
|
||||
if (hideAmount) {
|
||||
return formatAmountToLocalizedNumeralsWithCurrency(DISPLAY_HIDDEN_AMOUNT, currencyCode);
|
||||
@@ -293,15 +295,18 @@ export function useTransactionListPageBase() {
|
||||
}
|
||||
|
||||
function getDisplayTime(transaction: Transaction): string {
|
||||
return formatUnixTimeToShortTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
return formatDateTimeToShortTime(dateTime);
|
||||
}
|
||||
|
||||
function getDisplayLongDate(transaction: Transaction): string {
|
||||
return formatUnixTimeToLongDate(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
return formatDateTimeToLongDate(dateTime);
|
||||
}
|
||||
|
||||
function getDisplayLongYearMonth(transactionMonthList: TransactionMonthList): string {
|
||||
return formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthFirstUnixTime(transactionMonthList.yearDashMonth));
|
||||
const yearMonthDateTime = parseDateTimeFromUnixTime(getYearMonthFirstUnixTime(transactionMonthList.yearDashMonth));
|
||||
return formatDateTimeToGregorianLikeLongYearMonth(yearMonthDateTime);
|
||||
}
|
||||
|
||||
function getDisplayTimezone(transaction: Transaction): string {
|
||||
@@ -310,8 +315,10 @@ export function useTransactionListPageBase() {
|
||||
}
|
||||
|
||||
function getDisplayTimeInDefaultTimezone(transaction: Transaction): string {
|
||||
const utcOffset = numeralSystem.value.replaceWesternArabicDigitsToLocalizedDigits(getTimezoneOffset(settingsStore.appSettings.timeZone));
|
||||
return `${formatUnixTimeToLongDateTime(transaction.time)} (UTC${utcOffset})`;
|
||||
const timezoneOffsetMinutes = getTimezoneOffsetMinutes(transaction.time);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, timezoneOffsetMinutes);
|
||||
const utcOffset = numeralSystem.value.replaceWesternArabicDigitsToLocalizedDigits(getUtcOffsetByUtcOffsetMinutes(timezoneOffsetMinutes));
|
||||
return `${formatDateTimeToLongDateTime(dateTime)} (UTC${utcOffset})`;
|
||||
}
|
||||
|
||||
function getDisplayAmount(transaction: Transaction): string {
|
||||
@@ -372,7 +379,6 @@ export function useTransactionListPageBase() {
|
||||
customMaxDatetime,
|
||||
currentCalendarDate,
|
||||
// computed states
|
||||
currentTimezoneOffsetMinutes,
|
||||
firstDayOfWeek,
|
||||
fiscalYearStart,
|
||||
defaultCurrency,
|
||||
@@ -410,6 +416,7 @@ export function useTransactionListPageBase() {
|
||||
canAddTransaction,
|
||||
// functions
|
||||
hasSubCategoryInQuery,
|
||||
isSameAsDefaultTimezoneOffsetMinutes,
|
||||
getDisplayTime,
|
||||
getDisplayLongDate,
|
||||
getDisplayLongYearMonth,
|
||||
|
||||
@@ -202,12 +202,10 @@ import { useAccountsStore } from '@/stores/account.ts';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useOverviewStore } from '@/stores/overview.ts';
|
||||
|
||||
import { entries } from '@/core/base.ts';
|
||||
import { type NumeralSystem } from '@/core/numeral.ts';
|
||||
import { DateRange } from '@/core/datetime.ts';
|
||||
import { ThemeType } from '@/core/theme.ts';
|
||||
import {
|
||||
type TransactionAmountsRequestType,
|
||||
type TransactionMonthlyIncomeAndExpenseData,
|
||||
LATEST_12MONTHS_TRANSACTION_AMOUNTS_REQUEST_TYPES
|
||||
} from '@/models/transaction.ts';
|
||||
@@ -280,8 +278,7 @@ const monthlyIncomeAndExpenseData = computed<TransactionMonthlyIncomeAndExpenseD
|
||||
return data;
|
||||
}
|
||||
|
||||
for (const [type, monthDiff] of entries(LATEST_12MONTHS_TRANSACTION_AMOUNTS_REQUEST_TYPES)) {
|
||||
const amountRequestType = type as TransactionAmountsRequestType;
|
||||
for (const amountRequestType of LATEST_12MONTHS_TRANSACTION_AMOUNTS_REQUEST_TYPES) {
|
||||
const dateRange = overviewStore.transactionDataRange[amountRequestType];
|
||||
|
||||
if (!dateRange) {
|
||||
@@ -292,7 +289,6 @@ const monthlyIncomeAndExpenseData = computed<TransactionMonthlyIncomeAndExpenseD
|
||||
|
||||
data.push({
|
||||
monthStartTime: dateRange.startTime,
|
||||
monthsBeforeCurrentMonth: monthDiff,
|
||||
incomeAmount: item?.incomeAmount || 0,
|
||||
expenseAmount: item?.expenseAmount || 0,
|
||||
incompleteIncomeAmount: item ? item.incompleteIncomeAmount : true,
|
||||
|
||||
@@ -145,7 +145,9 @@
|
||||
<date-time-select
|
||||
:disabled="loading || submitting"
|
||||
:label="tt('Balance Time')"
|
||||
v-model="selectedAccount.balanceTime"
|
||||
:timezone-utc-offset="getDefaultTimezoneOffsetMinutes(selectedAccount)"
|
||||
:model-value="selectedAccount.balanceTime"
|
||||
@update:model-value="updateAccountBalanceTime(selectedAccount, $event)"
|
||||
@error="onShowDateTimeError" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="12">
|
||||
@@ -210,7 +212,6 @@ import { ALL_ACCOUNT_COLORS } from '@/consts/color.ts';
|
||||
import { Account } from '@/models/account.ts';
|
||||
|
||||
import { isNumber } from '@/lib/common.ts';
|
||||
import { getCurrentUnixTime } from '@/lib/datetime.ts';
|
||||
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||
|
||||
import {
|
||||
@@ -242,6 +243,9 @@ const {
|
||||
allAccountTypes,
|
||||
allAvailableMonthDays,
|
||||
isAccountSupportCreditCardStatementDate,
|
||||
getCurrentUnixTimeForNewAccount,
|
||||
getDefaultTimezoneOffsetMinutes,
|
||||
updateAccountBalanceTime,
|
||||
isNewAccount,
|
||||
addSubAccount,
|
||||
setAccount
|
||||
@@ -275,7 +279,7 @@ const accountAmountTitle = computed<string>(() => {
|
||||
|
||||
const isAccountModified = computed<boolean>(() => {
|
||||
if (!editAccountId.value) {
|
||||
return !account.value.equals(Account.createNewAccount(userStore.currentUserDefaultCurrency, account.value.balanceTime ?? getCurrentUnixTime()));
|
||||
return !account.value.equals(Account.createNewAccount(userStore.currentUserDefaultCurrency, account.value.balanceTime ?? getCurrentUnixTimeForNewAccount()));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
@@ -289,7 +293,7 @@ function open(options?: { id?: string, currentAccount?: Account, category?: numb
|
||||
loading.value = true;
|
||||
submitting.value = false;
|
||||
|
||||
const newAccount = Account.createNewAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTime());
|
||||
const newAccount = Account.createNewAccount(userStore.currentUserDefaultCurrency, getCurrentUnixTimeForNewAccount());
|
||||
account.value.fillFrom(newAccount);
|
||||
subAccounts.value = [];
|
||||
currentAccountIndex.value = -1;
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
<template #item.time="{ item }">
|
||||
<span>{{ getDisplayDateTime(item) }}</span>
|
||||
<v-chip class="ms-1" variant="flat" color="secondary" size="x-small"
|
||||
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
v-if="!isSameAsDefaultTimezoneOffsetMinutes(item)">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.type="{ item }">
|
||||
<v-chip label variant="outlined" size="x-small"
|
||||
@@ -323,7 +323,6 @@ const {
|
||||
startTime,
|
||||
endTime,
|
||||
reconciliationStatements,
|
||||
currentTimezoneOffsetMinutes,
|
||||
fiscalYearStart,
|
||||
allChartTypes,
|
||||
allDateAggregationTypes,
|
||||
@@ -341,6 +340,7 @@ const {
|
||||
setReconciliationStatements,
|
||||
getDisplayTransactionType,
|
||||
getDisplayDateTime,
|
||||
isSameAsDefaultTimezoneOffsetMinutes,
|
||||
getDisplayTimezone,
|
||||
getDisplaySourceAmount,
|
||||
getDisplayDestinationAmount,
|
||||
|
||||
@@ -151,9 +151,10 @@ import {
|
||||
} from '@/models/transaction.ts';
|
||||
|
||||
import {
|
||||
parseDateTimeFromUnixTime,
|
||||
getShiftedDateRangeAndDateType,
|
||||
getDateTypeByDateRange,
|
||||
getDateRangeByDateType,
|
||||
getDateRangeByDateType
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
import {
|
||||
@@ -188,7 +189,7 @@ const {
|
||||
tt,
|
||||
getAllDateRanges,
|
||||
getCurrentNumeralSystemType,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatDateTimeToLongDateTime,
|
||||
formatDateRange
|
||||
} = useI18n();
|
||||
|
||||
@@ -221,8 +222,8 @@ const filteredTransactions = computed<TransactionInsightDataItem[]>(() => explor
|
||||
const allDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.InsightsExplore, true));
|
||||
const canShiftDateRange = computed<boolean>(() => query.value.dateRangeType !== DateRange.All.type);
|
||||
const displayQueryDateRangeName = computed<string>(() => formatDateRange(query.value.dateRangeType, query.value.startTime, query.value.endTime));
|
||||
const displayQueryStartTime = computed<string>(() => formatUnixTimeToLongDateTime(query.value.startTime));
|
||||
const displayQueryEndTime = computed<string>(() => formatUnixTimeToLongDateTime(query.value.endTime));
|
||||
const displayQueryStartTime = computed<string>(() => formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.startTime)));
|
||||
const displayQueryEndTime = computed<string>(() => formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(query.value.endTime)));
|
||||
|
||||
const allTabs = computed<{ name: string, value: ExplorePageTabType }[]>(() => {
|
||||
return [
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<template #item.time="{ item }">
|
||||
<span>{{ getDisplayDateTime(item) }}</span>
|
||||
<v-chip class="ms-1" variant="flat" color="secondary" size="x-small"
|
||||
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
v-if="!isSameAsDefaultTimezoneOffsetMinutes(item)">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.type="{ item }">
|
||||
<v-chip label variant="outlined" size="x-small"
|
||||
@@ -76,7 +76,6 @@ import { ref, computed } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useExploresStore } from '@/stores/explore.ts';
|
||||
|
||||
@@ -89,7 +88,7 @@ import {
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffsetMinutes,
|
||||
parseDateTimeFromUnixTime
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
import {
|
||||
@@ -109,19 +108,17 @@ const emit = defineEmits<{
|
||||
|
||||
const {
|
||||
tt,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatUnixTimeToGregorianDefaultDateTime,
|
||||
formatDateTimeToLongDateTime,
|
||||
formatDateTimeToGregorianDefaultDateTime,
|
||||
formatAmountToWesternArabicNumeralsWithoutDigitGrouping,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const userStore = useUserStore();
|
||||
const exploresStore = useExploresStore();
|
||||
|
||||
const currentPage = ref<number>(1);
|
||||
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||
|
||||
const filteredTransactions = computed<TransactionInsightDataItem[]>(() => exploresStore.filteredTransactions);
|
||||
@@ -163,7 +160,12 @@ const dataTableHeaders = computed<object[]>(() => {
|
||||
});
|
||||
|
||||
function getDisplayDateTime(transaction: TransactionInsightDataItem): string {
|
||||
return formatUnixTimeToLongDateTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
return formatDateTimeToLongDateTime(dateTime);
|
||||
}
|
||||
|
||||
function isSameAsDefaultTimezoneOffsetMinutes(transaction: TransactionInsightDataItem): boolean {
|
||||
return transaction.utcOffset === getTimezoneOffsetMinutes(transaction.time);
|
||||
}
|
||||
|
||||
function getDisplayTimezone(transaction: TransactionInsightDataItem): string {
|
||||
@@ -234,7 +236,7 @@ function buildExportResults(): { headers: string[], data: string[][] } | undefin
|
||||
],
|
||||
data: filteredTransactions.value
|
||||
.map(transaction => {
|
||||
const transactionTime = parseDateTimeFromUnixTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value).getUnixTime();
|
||||
const transactionTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset);
|
||||
const type = getDisplayTransactionType(transaction);
|
||||
|
||||
let categoryName = transaction.secondaryCategoryName;
|
||||
@@ -254,7 +256,7 @@ function buildExportResults(): { headers: string[], data: string[][] } | undefin
|
||||
const description = transaction.comment || '';
|
||||
|
||||
return [
|
||||
formatUnixTimeToGregorianDefaultDateTime(transactionTime),
|
||||
formatDateTimeToGregorianDefaultDateTime(transactionTime),
|
||||
type,
|
||||
categoryName,
|
||||
displayAmount,
|
||||
|
||||
@@ -41,7 +41,7 @@ import { DISPLAY_HIDDEN_AMOUNT, INCOMPLETE_AMOUNT_SUFFIX } from '@/consts/numera
|
||||
|
||||
import { type TransactionMonthlyIncomeAndExpenseData } from '@/models/transaction.ts';
|
||||
|
||||
import { getUnixTimeBeforeUnixTime, getThisMonthFirstUnixTime } from '@/lib/datetime.ts';
|
||||
import { parseDateTimeFromUnixTime } from '@/lib/datetime.ts';
|
||||
import { getExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts';
|
||||
|
||||
export interface MonthlyIncomeAndExpenseCardClickEvent {
|
||||
@@ -64,7 +64,7 @@ const emit = defineEmits<{
|
||||
const {
|
||||
tt,
|
||||
getCurrentLanguageTextDirection,
|
||||
formatUnixTimeToGregorianLikeShortMonth,
|
||||
formatDateTimeToGregorianLikeShortMonth,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
@@ -98,11 +98,9 @@ const chartOptions = computed<object>(() => {
|
||||
const expenseIncomeAmountColor = getExpenseAndIncomeAmountColor(userStore.currentUserExpenseAmountColor, userStore.currentUserIncomeAmountColor, props.isDarkMode);
|
||||
|
||||
if (props.data) {
|
||||
const currentMonthFirstUnixTime = getThisMonthFirstUnixTime();
|
||||
|
||||
for (const item of props.data) {
|
||||
const monthFirstUnixTime = getUnixTimeBeforeUnixTime(currentMonthFirstUnixTime, item.monthsBeforeCurrentMonth, 'months');
|
||||
const monthShortName = formatUnixTimeToGregorianLikeShortMonth(monthFirstUnixTime);
|
||||
const monthStartDateTime = parseDateTimeFromUnixTime(item.monthStartTime);
|
||||
const monthShortName = formatDateTimeToGregorianLikeShortMonth(monthStartDateTime);
|
||||
|
||||
monthNames.push(monthShortName);
|
||||
incomeAmounts.push(item.incomeAmount);
|
||||
|
||||
@@ -534,8 +534,8 @@
|
||||
<td class="transaction-table-column-time">
|
||||
<div class="d-flex flex-column">
|
||||
<span>{{ getDisplayTime(transaction) }}</span>
|
||||
<span class="text-caption" v-if="transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(transaction) }}</span>
|
||||
<v-tooltip activator="parent" v-if="transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimeInDefaultTimezone(transaction) }}</v-tooltip>
|
||||
<span class="text-caption" v-if="!isSameAsDefaultTimezoneOffsetMinutes(transaction)">{{ getDisplayTimezone(transaction) }}</span>
|
||||
<v-tooltip activator="parent" v-if="!isSameAsDefaultTimezoneOffsetMinutes(transaction)">{{ getDisplayTimeInDefaultTimezone(transaction) }}</v-tooltip>
|
||||
</div>
|
||||
</td>
|
||||
<td class="transaction-table-column-category">
|
||||
@@ -691,8 +691,6 @@ import {
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
parseDateTimeFromUnixTime,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getActualUnixTimeForStore,
|
||||
getDayFirstUnixTimeBySpecifiedUnixTime,
|
||||
getYearMonthFirstUnixTime,
|
||||
getYearMonthLastUnixTime,
|
||||
@@ -775,7 +773,6 @@ const {
|
||||
customMinDatetime,
|
||||
customMaxDatetime,
|
||||
currentCalendarDate,
|
||||
currentTimezoneOffsetMinutes,
|
||||
firstDayOfWeek,
|
||||
fiscalYearStart,
|
||||
defaultCurrency,
|
||||
@@ -809,6 +806,7 @@ const {
|
||||
transactionCalendarMaxDate,
|
||||
currentMonthTransactionData,
|
||||
hasSubCategoryInQuery,
|
||||
isSameAsDefaultTimezoneOffsetMinutes,
|
||||
canAddTransaction,
|
||||
getDisplayTime,
|
||||
getDisplayLongDate,
|
||||
@@ -1234,7 +1232,7 @@ function changePageType(type: number): void {
|
||||
function changeDateFilter(dateRange: TimeRangeAndDateType | number | null): void {
|
||||
if (dateRange === DateRange.Custom.type || (isObject(dateRange) && dateRange.dateType === DateRange.Custom.type && !dateRange.minTime && !dateRange.maxTime)) { // Custom
|
||||
if (!query.value.minTime || !query.value.maxTime) {
|
||||
customMaxDatetime.value = getActualUnixTimeForStore(getCurrentUnixTime(), currentTimezoneOffsetMinutes.value, getBrowserTimezoneOffsetMinutes());
|
||||
customMaxDatetime.value = getCurrentUnixTime();
|
||||
customMinDatetime.value = getDayFirstUnixTimeBySpecifiedUnixTime(customMaxDatetime.value);
|
||||
} else {
|
||||
customMaxDatetime.value = query.value.maxTime;
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<template #item.time="{ item }">
|
||||
<span>{{ getDisplayDateTime(item) }}</span>
|
||||
<v-chip class="ms-1" variant="flat" color="grey" size="x-small"
|
||||
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
v-if="!isSameAsDefaultTimezoneOffsetMinutes(item)">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.type="{ value }">
|
||||
<v-chip label color="secondary" variant="outlined" size="x-small" v-if="value === TransactionType.ModifyBalance">{{ tt('Modify Balance') }}</v-chip>
|
||||
@@ -427,7 +427,9 @@ import {
|
||||
} from '@/lib/common.ts';
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
getTimezoneOffsetMinutes
|
||||
getTimezoneOffsetMinutes,
|
||||
parseDateTimeFromUnixTime,
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset
|
||||
} from '@/lib/datetime.ts';
|
||||
import { formatCoordinate } from '@/lib/coordinate.ts';
|
||||
import {
|
||||
@@ -495,7 +497,7 @@ const props = defineProps<{
|
||||
const {
|
||||
tt,
|
||||
getCurrentNumeralSystemType,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatDateTimeToLongDateTime,
|
||||
formatAmountToLocalizedNumeralsWithCurrency,
|
||||
getCategorizedAccountsWithDisplayBalance
|
||||
} = useI18n();
|
||||
@@ -536,7 +538,6 @@ const currentDescriptionFilterValue = ref<string | null>(null);
|
||||
|
||||
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
|
||||
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
|
||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||
|
||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||
const coordinateDisplayType = computed<number>(() => userStore.currentUserCoordinateDisplayType);
|
||||
@@ -1129,8 +1130,8 @@ const displayFilterCustomDateRange = computed<string>(() => {
|
||||
return '';
|
||||
}
|
||||
|
||||
const minDisplayTime = formatUnixTimeToLongDateTime(filters.value.minDatetime);
|
||||
const maxDisplayTime = formatUnixTimeToLongDateTime(filters.value.maxDatetime);
|
||||
const minDisplayTime = formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(filters.value.minDatetime));
|
||||
const maxDisplayTime = formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(filters.value.maxDatetime));
|
||||
|
||||
return `${minDisplayTime} - ${maxDisplayTime}`
|
||||
});
|
||||
@@ -1272,7 +1273,12 @@ function isTagValid(tagIds: string[], tagIndex: number): boolean {
|
||||
}
|
||||
|
||||
function getDisplayDateTime(transaction: ImportTransaction): string {
|
||||
return formatUnixTimeToLongDateTime(transaction.time, transaction.utcOffset, currentTimezoneOffsetMinutes.value);
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.time, transaction.utcOffset)
|
||||
return formatDateTimeToLongDateTime(dateTime);
|
||||
}
|
||||
|
||||
function isSameAsDefaultTimezoneOffsetMinutes(transaction: ImportTransaction): boolean {
|
||||
return transaction.utcOffset === getTimezoneOffsetMinutes(transaction.time);
|
||||
}
|
||||
|
||||
function getDisplayTimezone(transaction: ImportTransaction): string {
|
||||
|
||||
+5
-2
@@ -54,7 +54,10 @@ import { KnownFileType } from '@/core/file.ts';
|
||||
import type { ImportTransactionRequest, ImportTransactionRequestItem } from '@/models/imported_transaction.ts';
|
||||
|
||||
import { isDefined } from '@/lib/common.ts';
|
||||
import { getBrowserTimezoneOffsetMinutes } from '@/lib/datetime.ts';
|
||||
import {
|
||||
getTimezoneOffsetMinutes,
|
||||
getCurrentUnixTime
|
||||
} from '@/lib/datetime.ts';
|
||||
import {
|
||||
openTextFileContent,
|
||||
startDownloadFile
|
||||
@@ -121,7 +124,7 @@ function parse(row, index) {
|
||||
|
||||
return {
|
||||
time: row[0], // ${tt('sample.importTransactionCustomScript.fieldTimeDescription')}
|
||||
utcOffset: '${getBrowserTimezoneOffsetMinutes()}', // ${tt('sample.importTransactionCustomScript.fieldUtcOffsetDescription')}
|
||||
utcOffset: '${getTimezoneOffsetMinutes(getCurrentUnixTime())}', // ${tt('sample.importTransactionCustomScript.fieldUtcOffsetDescription')}
|
||||
type: TransactionType.Expense, // ${tt('sample.importTransactionCustomScript.fieldTypeDescription')}
|
||||
categoryName: row[4], // ${tt('sample.importTransactionCustomScript.fieldCategoryNameDescription')}
|
||||
sourceAccountName: row[5], // ${tt('sample.importTransactionCustomScript.fieldSourceAccountNameDescription')}
|
||||
|
||||
@@ -250,7 +250,9 @@
|
||||
:readonly="mode === TransactionEditPageMode.View"
|
||||
:disabled="loading || submitting || (mode === TransactionEditPageMode.Edit && transaction.type === TransactionType.ModifyBalance)"
|
||||
:label="tt('Transaction Time')"
|
||||
v-model="transaction.time"
|
||||
:timezone-utc-offset="transaction.utcOffset"
|
||||
:model-value="transaction.time"
|
||||
@update:model-value="updateTransactionTime"
|
||||
@error="onShowDateTimeError" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" v-if="type === TransactionEditPageType.Template && transaction instanceof TransactionTemplate && transaction.templateType === TemplateType.Schedule.type">
|
||||
@@ -274,7 +276,8 @@
|
||||
:placeholder="!transaction.timeZone && transaction.timeZone !== '' ? `(${transactionDisplayTimezone}) ${transactionTimezoneTimeDifference}` : tt('Timezone')"
|
||||
:items="allTimezones"
|
||||
:no-data-text="tt('No results')"
|
||||
v-model="transaction.timeZone"
|
||||
:model-value="transaction.timeZone"
|
||||
@update:model-value="updateTransactionTimezone"
|
||||
>
|
||||
<template #selection="{ item }">
|
||||
<span class="text-truncate" v-if="transaction.timeZone || transaction.timeZone === ''">
|
||||
@@ -642,6 +645,8 @@ const {
|
||||
inputEmptyProblemMessage,
|
||||
inputIsEmpty,
|
||||
createNewTransactionModel,
|
||||
updateTransactionTime,
|
||||
updateTransactionTimezone,
|
||||
swapTransactionData,
|
||||
getTransactionPictureUrl
|
||||
} = useTransactionEditPageBase(props.type);
|
||||
@@ -714,7 +719,7 @@ const isTransactionModified = computed<boolean>(() => {
|
||||
}
|
||||
});
|
||||
|
||||
function setTransaction(newTransaction: Transaction | null, options: SetTransactionOptions, setContextData: boolean, convertContextTime: boolean): void {
|
||||
function setTransaction(newTransaction: Transaction | null, options: SetTransactionOptions, setContextData: boolean): void {
|
||||
setTransactionModelByTransaction(
|
||||
transaction.value,
|
||||
newTransaction,
|
||||
@@ -735,8 +740,7 @@ function setTransaction(newTransaction: Transaction | null, options: SetTransact
|
||||
tagIds: options.tagIds,
|
||||
comment: options.comment
|
||||
},
|
||||
setContextData,
|
||||
convertContextTime
|
||||
setContextData
|
||||
);
|
||||
}
|
||||
|
||||
@@ -758,7 +762,7 @@ function open(options: TransactionEditOptions): Promise<TransactionEditResponse
|
||||
initTagIds.value = options.tagIds;
|
||||
|
||||
const newTransaction = createNewTransactionModel(options.type);
|
||||
setTransaction(newTransaction, options, true, false);
|
||||
setTransaction(newTransaction, options, true);
|
||||
|
||||
const promises: Promise<unknown>[] = [
|
||||
accountsStore.loadAllAccounts({ force: false }),
|
||||
@@ -769,7 +773,7 @@ function open(options: TransactionEditOptions): Promise<TransactionEditResponse
|
||||
if (props.type === TransactionEditPageType.Transaction) {
|
||||
if (options && options.id) {
|
||||
if (options.currentTransaction) {
|
||||
setTransaction(options.currentTransaction, options, true, true);
|
||||
setTransaction(options.currentTransaction, options, true);
|
||||
}
|
||||
|
||||
mode.value = TransactionEditPageMode.View;
|
||||
@@ -781,10 +785,10 @@ function open(options: TransactionEditOptions): Promise<TransactionEditResponse
|
||||
editId.value = null;
|
||||
|
||||
if (options.template) {
|
||||
setTransaction(options.template, options, false, false);
|
||||
setTransaction(options.template, options, false);
|
||||
addByTemplateId.value = options.template.id;
|
||||
} else if (!options.noTransactionDraft && (settingsStore.appSettings.autoSaveTransactionDraft === 'enabled' || settingsStore.appSettings.autoSaveTransactionDraft === 'confirmation') && transactionsStore.transactionDraft) {
|
||||
setTransaction(Transaction.ofDraft(transactionsStore.transactionDraft), options, false, false);
|
||||
setTransaction(Transaction.ofDraft(transactionsStore.transactionDraft), options, false);
|
||||
}
|
||||
|
||||
if (settingsStore.appSettings.autoGetCurrentGeoLocation
|
||||
@@ -809,7 +813,7 @@ function open(options: TransactionEditOptions): Promise<TransactionEditResponse
|
||||
|
||||
if (options && options.id) {
|
||||
if (options.currentTemplate) {
|
||||
setTransaction(options.currentTemplate, options, false, false);
|
||||
setTransaction(options.currentTemplate, options, false);
|
||||
(transaction.value as TransactionTemplate).fillFrom(options.currentTemplate);
|
||||
}
|
||||
|
||||
@@ -850,11 +854,11 @@ function open(options: TransactionEditOptions): Promise<TransactionEditResponse
|
||||
|
||||
if (props.type === TransactionEditPageType.Transaction && options && options.id && responses[3] && responses[3] instanceof Transaction) {
|
||||
const transaction: Transaction = responses[3];
|
||||
setTransaction(transaction, options, true, true);
|
||||
setTransaction(transaction, options, true);
|
||||
originalTransactionEditable.value = transaction.editable;
|
||||
} else if (props.type === TransactionEditPageType.Template && options && options.id && responses[3] && responses[3] instanceof TransactionTemplate) {
|
||||
const template: TransactionTemplate = responses[3];
|
||||
setTransaction(template, options, false, false);
|
||||
setTransaction(template, options, false);
|
||||
|
||||
if (!(transaction.value instanceof TransactionTemplate)) {
|
||||
transaction.value = TransactionTemplate.createNewTransactionTemplate(transaction.value);
|
||||
@@ -862,7 +866,7 @@ function open(options: TransactionEditOptions): Promise<TransactionEditResponse
|
||||
|
||||
(transaction.value as TransactionTemplate).fillFrom(template);
|
||||
} else {
|
||||
setTransaction(null, options, true, true);
|
||||
setTransaction(null, options, true);
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
@@ -1003,7 +1007,7 @@ function duplicate(withTime?: boolean, withGeoLocation?: boolean): void {
|
||||
if (!withTime) {
|
||||
transaction.value.time = getCurrentUnixTime();
|
||||
transaction.value.timeZone = settingsStore.appSettings.timeZone;
|
||||
transaction.value.utcOffset = getTimezoneOffsetMinutes(transaction.value.timeZone);
|
||||
transaction.value.utcOffset = getTimezoneOffsetMinutes(transaction.value.time, transaction.value.timeZone);
|
||||
}
|
||||
|
||||
if (!withGeoLocation) {
|
||||
|
||||
@@ -218,6 +218,7 @@ import { type UserExternalAuthInfoResponse } from '@/models/user_external_auth.t
|
||||
import { type TokenInfoResponse, SessionDeviceType, SessionInfo } from '@/models/token.ts';
|
||||
|
||||
import { isEquals } from '@/lib/common.ts';
|
||||
import { parseDateTimeFromUnixTime } from '@/lib/datetime.ts';
|
||||
import { parseSessionInfo } from '@/lib/session.ts';
|
||||
import {
|
||||
isAPITokenEnabled,
|
||||
@@ -253,7 +254,7 @@ class DesktopPageLinkedThirdPartyLogin {
|
||||
this.externalAuthType = externalAuthInfoResponse.externalAuthType;
|
||||
this.linked = externalAuthInfoResponse.linked;
|
||||
this.externalUsername = externalAuthInfoResponse.externalUsername ? externalAuthInfoResponse.externalUsername : '-';
|
||||
this.createdAt = externalAuthInfoResponse.createdAt ? formatUnixTimeToLongDateTime(externalAuthInfoResponse.createdAt) : '-';
|
||||
this.createdAt = externalAuthInfoResponse.createdAt ? formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(externalAuthInfoResponse.createdAt)) : '-';
|
||||
|
||||
if (externalAuthInfoResponse.externalAuthCategory === 'oauth2') {
|
||||
this.displayName = getLocalizedOAuth2ProviderName(externalAuthInfoResponse.externalAuthType, getOIDCCustomDisplayNames());
|
||||
@@ -277,7 +278,7 @@ class DesktopPageSessionInfo extends SessionInfo {
|
||||
public constructor(sessionInfo: SessionInfo) {
|
||||
super(sessionInfo.tokenId, sessionInfo.isCurrent, sessionInfo.deviceType, sessionInfo.deviceInfo, sessionInfo.deviceName, sessionInfo.lastSeen);
|
||||
this.icon = this.getTokenIcon(sessionInfo.deviceType);
|
||||
this.lastSeenDateTime = sessionInfo.lastSeen ? formatUnixTimeToLongDateTime(sessionInfo.lastSeen) : '-';
|
||||
this.lastSeenDateTime = sessionInfo.lastSeen ? formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(sessionInfo.lastSeen)) : '-';
|
||||
}
|
||||
|
||||
private getTokenIcon(deviceType: SessionDeviceType): string {
|
||||
@@ -306,7 +307,7 @@ type SnackBarType = InstanceType<typeof SnackBar>;
|
||||
|
||||
const {
|
||||
tt,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatDateTimeToLongDateTime,
|
||||
getLocalizedOAuth2ProviderName,
|
||||
setLanguage
|
||||
} = useI18n();
|
||||
|
||||
@@ -129,6 +129,7 @@ import { useUserStore } from '@/stores/user.ts';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||
|
||||
import { findNameByValue } from '@/lib/common.ts';
|
||||
import { parseDateTimeFromUnixTime } from '@/lib/datetime.ts';
|
||||
import { getClientDisplayVersion, getDesktopVersionPath } from '@/lib/version.ts';
|
||||
import { isUserScheduledTransactionEnabled } from '@/lib/server_settings.ts';
|
||||
import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts';
|
||||
@@ -137,7 +138,7 @@ const props = defineProps<{
|
||||
f7router: Router.Router;
|
||||
}>();
|
||||
|
||||
const { tt, formatUnixTimeToLongDate, initLocale } = useI18n();
|
||||
const { tt, formatDateTimeToLongDate, initLocale } = useI18n();
|
||||
const { showToast, showConfirm } = useI18nUIComponents();
|
||||
const { allThemes, allTimezones, timeZone, isAutoUpdateExchangeRatesData, showAccountBalance } = useAppSettingPageBase();
|
||||
|
||||
@@ -197,8 +198,12 @@ const isEnableAnimate = computed<boolean>({
|
||||
const isEnableApplicationLock = computed<boolean>(() => settingsStore.appSettings.applicationLock);
|
||||
|
||||
const exchangeRatesLastUpdateDate = computed<string>(() => {
|
||||
const exchangeRatesLastUpdateTime = exchangeRatesStore.exchangeRatesLastUpdateTime;
|
||||
return exchangeRatesLastUpdateTime ? formatUnixTimeToLongDate(exchangeRatesLastUpdateTime) : '';
|
||||
if (!exchangeRatesStore.exchangeRatesLastUpdateTime) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const exchangeRatesLastUpdateTime = parseDateTimeFromUnixTime(exchangeRatesStore.exchangeRatesLastUpdateTime);
|
||||
return formatDateTimeToLongDate(exchangeRatesLastUpdateTime);
|
||||
});
|
||||
|
||||
function switchToDesktopVersion(): void {
|
||||
|
||||
@@ -233,8 +233,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<date-time-selection-sheet :init-mode="accountContext.balanceDateTimeSheetMode"
|
||||
:timezone-utc-offset="getDefaultTimezoneOffsetMinutes(account)"
|
||||
:model-value="account.balanceTime"
|
||||
v-model:show="accountContext.showBalanceDateTimeSheet"
|
||||
v-model="account.balanceTime">
|
||||
@update:model-value="updateAccountBalanceTime(account, $event)">
|
||||
</date-time-selection-sheet>
|
||||
</f7-list-item>
|
||||
|
||||
@@ -475,8 +477,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<date-time-selection-sheet :init-mode="subAccountContexts[idx]!.balanceDateTimeSheetMode"
|
||||
:timezone-utc-offset="getDefaultTimezoneOffsetMinutes(subAccount)"
|
||||
:model-value="subAccount.balanceTime"
|
||||
v-model:show="subAccountContexts[idx]!.showBalanceDateTimeSheet"
|
||||
v-model="subAccount.balanceTime">
|
||||
@update:model-value="updateAccountBalanceTime(subAccount, $event)">
|
||||
</date-time-selection-sheet>
|
||||
</f7-list-item>
|
||||
|
||||
@@ -538,8 +542,7 @@ import { isDefined, findDisplayNameByType } from '@/lib/common.ts';
|
||||
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||
import {
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getActualUnixTimeForStore
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
interface AccountContext {
|
||||
@@ -561,8 +564,8 @@ const {
|
||||
tt,
|
||||
getAllCurrencies,
|
||||
getCurrencyName,
|
||||
formatUnixTimeToLongDate,
|
||||
formatUnixTimeToLongTime,
|
||||
formatDateTimeToLongDate,
|
||||
formatDateTimeToLongTime,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
@@ -582,7 +585,9 @@ const {
|
||||
allAccountTypes,
|
||||
allAvailableMonthDays,
|
||||
isAccountSupportCreditCardStatementDate,
|
||||
getDefaultTimezoneOffsetMinutes,
|
||||
getAccountCreditCardStatementDate,
|
||||
updateAccountBalanceTime,
|
||||
isNewAccount,
|
||||
addSubAccount,
|
||||
setAccount
|
||||
@@ -621,7 +626,8 @@ function formatAccountBalanceDate(account: Account): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
return formatUnixTimeToLongDate(getActualUnixTimeForStore(account.balanceTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(account.balanceTime, getTimezoneOffsetMinutes(account.balanceTime));
|
||||
return formatDateTimeToLongDate(dateTime);
|
||||
}
|
||||
|
||||
function formatAccountBalanceTime(account: Account): string {
|
||||
@@ -629,7 +635,8 @@ function formatAccountBalanceTime(account: Account): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
return formatUnixTimeToLongTime(getActualUnixTimeForStore(account.balanceTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(account.balanceTime, getTimezoneOffsetMinutes(account.balanceTime));
|
||||
return formatDateTimeToLongTime(dateTime);
|
||||
}
|
||||
|
||||
function init(): void {
|
||||
|
||||
@@ -51,10 +51,10 @@
|
||||
</template>
|
||||
<template #footer>
|
||||
<div v-if="dateRange.isUserCustomRange && queryDateRangeType === dateRange.type && startTime && endTime">
|
||||
<span>{{ displayStartTime }}</span>
|
||||
<span>{{ displayStartDateTime }}</span>
|
||||
<span> - </span>
|
||||
<br/>
|
||||
<span>{{ displayEndTime }}</span>
|
||||
<span>{{ displayEndDateTime }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
@@ -227,7 +227,7 @@
|
||||
<div class="transaction-footer display-flex justify-content-space-between">
|
||||
<div class="flex-shrink-0">
|
||||
<span>{{ getDisplayTime(item.transaction) }}</span>
|
||||
<span v-if="item.transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ `(${getDisplayTimezone(item.transaction)})` }}</span>
|
||||
<span style="margin-inline-start: 4px" v-if="!isSameAsDefaultTimezoneOffsetMinutes(item.transaction)">{{ `(${getDisplayTimezone(item.transaction)})` }}</span>
|
||||
</div>
|
||||
<div class="account-balance flex-shrink-1">
|
||||
<span>{{ isCurrentLiabilityAccount ? tt('Outstanding Balance') : tt('Balance') }}</span>
|
||||
@@ -388,7 +388,6 @@ const {
|
||||
tt,
|
||||
getCurrentLanguageTextDirection,
|
||||
getAllDateRanges,
|
||||
formatUnixTimeToLongDateTime,
|
||||
formatNumberToLocalizedNumerals
|
||||
} = useI18n();
|
||||
|
||||
@@ -402,7 +401,6 @@ const {
|
||||
firstDayOfWeek,
|
||||
fiscalYearStart,
|
||||
allDateAggregationTypes,
|
||||
currentTimezoneOffsetMinutes,
|
||||
isCurrentLiabilityAccount,
|
||||
currentAccount,
|
||||
currentAccountCurrency,
|
||||
@@ -416,6 +414,7 @@ const {
|
||||
setReconciliationStatements,
|
||||
getDisplayDate,
|
||||
getDisplayTime,
|
||||
isSameAsDefaultTimezoneOffsetMinutes,
|
||||
getDisplayTimezone,
|
||||
getDisplaySourceAmount,
|
||||
getDisplayDestinationAmount,
|
||||
@@ -446,8 +445,6 @@ const virtualDataItems = ref<ReconciliationStatementVirtualListData>({
|
||||
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
|
||||
const validQuery = computed(() => currentAccount.value && currentAccount.value.type === AccountType.SingleAccount.type);
|
||||
const allAvailableDateRanges = computed(() => getAllDateRanges(DateRangeScene.Normal, true, !!accountsStore.getAccountStatementDate(accountId.value)));
|
||||
const displayStartTime = computed<string>(() => formatUnixTimeToLongDateTime(startTime.value));
|
||||
const displayEndTime = computed<string>(() => formatUnixTimeToLongDateTime(endTime.value));
|
||||
|
||||
const allReconciliationStatementVirtualListItems = computed<ReconciliationStatementVirtualListItem[]>(() => {
|
||||
const ret: ReconciliationStatementVirtualListItem[] = [];
|
||||
|
||||
@@ -124,8 +124,9 @@ import { useI18n } from '@/locales/helpers.ts';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
|
||||
import { TextDirection } from '@/core/text.ts';
|
||||
import { type DateTime } from '@/core/datetime.ts';
|
||||
import { FontSize } from '@/core/font.ts';
|
||||
import { parseDateTimeFromUnixTime, getCurrentUnixTime } from '@/lib/datetime.ts';
|
||||
import { getCurrentDateTime } from '@/lib/datetime.ts';
|
||||
import { setAppFontSize, getFontSizePreviewClassName } from '@/lib/ui/mobile.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -136,23 +137,23 @@ const {
|
||||
tt,
|
||||
getCurrentLanguageTextDirection,
|
||||
getWeekdayShortName,
|
||||
getCalendarDisplayDayOfMonthFromUnixTime,
|
||||
formatUnixTimeToShortTime,
|
||||
formatUnixTimeToGregorianLikeLongYearMonth,
|
||||
getCalendarDisplayDayOfMonthFromDateTime,
|
||||
formatDateTimeToShortTime,
|
||||
formatDateTimeToGregorianLikeLongYearMonth,
|
||||
formatAmountToLocalizedNumeralsWithCurrency
|
||||
} = useI18n();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
const currentUnixTime = ref<number>(getCurrentUnixTime());
|
||||
const currentDateTime = ref<DateTime>(getCurrentDateTime());
|
||||
const fontSize = ref<number>(settingsStore.appSettings.fontSize);
|
||||
|
||||
const textDirection = computed<string>(() => getCurrentLanguageTextDirection());
|
||||
const fontSizePreviewClassName = computed<string>(() => getFontSizePreviewClassName(fontSize.value));
|
||||
const currentLongYearMonth = computed<string>(() => formatUnixTimeToGregorianLikeLongYearMonth(currentUnixTime.value));
|
||||
const currentDayOfMonth = computed<string>(() => getCalendarDisplayDayOfMonthFromUnixTime(currentUnixTime.value));
|
||||
const currentDayOfWeek = computed<string>(() => getWeekdayShortName(parseDateTimeFromUnixTime(currentUnixTime.value).getWeekDay()));
|
||||
const currentShortTime = computed<string>(() => formatUnixTimeToShortTime(currentUnixTime.value));
|
||||
const currentLongYearMonth = computed<string>(() => formatDateTimeToGregorianLikeLongYearMonth(currentDateTime.value));
|
||||
const currentDayOfMonth = computed<string>(() => getCalendarDisplayDayOfMonthFromDateTime(currentDateTime.value));
|
||||
const currentDayOfWeek = computed<string>(() => getWeekdayShortName(currentDateTime.value.getWeekDay()));
|
||||
const currentShortTime = computed<string>(() => formatDateTimeToShortTime(currentDateTime.value));
|
||||
|
||||
function getFontSizeName(): string {
|
||||
return '';
|
||||
|
||||
@@ -251,8 +251,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<date-time-selection-sheet :init-mode="transactionDateTimeSheetMode"
|
||||
:timezone-utc-offset="transaction.utcOffset"
|
||||
:model-value="transaction.time"
|
||||
v-model:show="showTransactionDateTimeSheet"
|
||||
v-model="transaction.time">
|
||||
@update:model-value="updateTransactionTime">
|
||||
</date-time-selection-sheet>
|
||||
</f7-list-item>
|
||||
|
||||
@@ -323,8 +325,9 @@
|
||||
:filter-placeholder="tt('Timezone')"
|
||||
:filter-no-items-text="tt('No results')"
|
||||
:items="allTimezones"
|
||||
:model-value="transaction.timeZone"
|
||||
v-model:show="showTimezonePopup"
|
||||
v-model="transaction.timeZone">
|
||||
@update:model-value="updateTransactionTimezone">
|
||||
</list-item-selection-popup>
|
||||
</f7-list-item>
|
||||
|
||||
@@ -512,10 +515,9 @@ import type { TransactionPictureInfoBasicResponse } from '@/models/transaction_p
|
||||
import { Transaction } from '@/models/transaction.ts';
|
||||
|
||||
import {
|
||||
getActualUnixTimeForStore,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getTimezoneOffset,
|
||||
getTimezoneOffsetMinutes
|
||||
getTimezoneOffsetMinutes,
|
||||
parseDateTimeFromUnixTimeWithTimezoneOffset
|
||||
} from '@/lib/datetime.ts';
|
||||
import { formatCoordinate } from '@/lib/coordinate.ts';
|
||||
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||
@@ -536,8 +538,8 @@ const {
|
||||
tt,
|
||||
getMultiMonthdayShortNames,
|
||||
getMultiWeekdayLongNames,
|
||||
formatUnixTimeToLongDate,
|
||||
formatUnixTimeToLongTime,
|
||||
formatDateTimeToLongDate,
|
||||
formatDateTimeToLongTime,
|
||||
formatGregorianTextualYearMonthDayToLongDate,
|
||||
parseAmountFromLocalizedNumerals
|
||||
} = useI18n();
|
||||
@@ -589,6 +591,8 @@ const {
|
||||
geoLocationStatusInfo,
|
||||
inputEmptyProblemMessage,
|
||||
inputIsEmpty,
|
||||
updateTransactionTime,
|
||||
updateTransactionTimezone,
|
||||
swapTransactionData,
|
||||
getDisplayAmount,
|
||||
getTransactionPictureUrl
|
||||
@@ -655,19 +659,23 @@ const destinationAmountClass = computed<Record<string, boolean>>(() => {
|
||||
|
||||
const transactionDisplayDate = computed<string>(() => {
|
||||
if (mode.value !== TransactionEditPageMode.View || !showTimeInDefaultTimezone.value) {
|
||||
return formatUnixTimeToLongDate(getActualUnixTimeForStore(transaction.value.time, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.value.time, transaction.value.utcOffset);
|
||||
return formatDateTimeToLongDate(dateTime);
|
||||
}
|
||||
|
||||
return formatUnixTimeToLongDate(getActualUnixTimeForStore(transaction.value.time, transaction.value.utcOffset, getBrowserTimezoneOffsetMinutes()));
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.value.time, getTimezoneOffsetMinutes(transaction.value.time));
|
||||
return formatDateTimeToLongDate(dateTime);
|
||||
});
|
||||
|
||||
const transactionDisplayTime = computed<string>(() => {
|
||||
if (mode.value !== TransactionEditPageMode.View || !showTimeInDefaultTimezone.value) {
|
||||
return formatUnixTimeToLongTime(getActualUnixTimeForStore(transaction.value.time, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.value.time, transaction.value.utcOffset);
|
||||
return formatDateTimeToLongTime(dateTime);
|
||||
}
|
||||
|
||||
const utcOffset = numeralSystem.value.replaceWesternArabicDigitsToLocalizedDigits(getTimezoneOffset(settingsStore.appSettings.timeZone));
|
||||
return `${formatUnixTimeToLongTime(getActualUnixTimeForStore(transaction.value.time, transaction.value.utcOffset, getBrowserTimezoneOffsetMinutes()))} (UTC${utcOffset})`;
|
||||
const dateTime = parseDateTimeFromUnixTimeWithTimezoneOffset(transaction.value.time, getTimezoneOffsetMinutes(transaction.value.time));
|
||||
const utcOffset = numeralSystem.value.replaceWesternArabicDigitsToLocalizedDigits(getTimezoneOffset(transaction.value.time));
|
||||
return `${formatDateTimeToLongTime(dateTime)} (UTC${utcOffset})`;
|
||||
});
|
||||
|
||||
const transactionDisplayTimezoneName = computed<string>(() => {
|
||||
@@ -949,7 +957,6 @@ function init(): void {
|
||||
tagIds: query['tagIds'],
|
||||
comment: query['comment']
|
||||
},
|
||||
pageTypeAndMode.type === TransactionEditPageType.Transaction && (mode.value === TransactionEditPageMode.Edit || mode.value === TransactionEditPageMode.View),
|
||||
pageTypeAndMode.type === TransactionEditPageType.Transaction && (mode.value === TransactionEditPageMode.Edit || mode.value === TransactionEditPageMode.View)
|
||||
);
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
<template #media>
|
||||
<div class="display-flex flex-direction-column transaction-date" :style="getTransactionDateStyle(transaction, idx > 0 ? transactionMonthList.items[idx - 1] : undefined)">
|
||||
<span class="transaction-day full-line flex-direction-column">
|
||||
{{ getCalendarDisplayDayOfMonthFromUnixTime(transaction.time) }}
|
||||
{{ transaction.gregorianCalendarDayOfMonth ? numeralSystem.formatNumber(transaction.gregorianCalendarDayOfMonth) : '' }}
|
||||
</span>
|
||||
<span class="transaction-day-of-week full-line flex-direction-column" v-if="transaction.displayDayOfWeek">
|
||||
{{ getWeekdayShortName(transaction.displayDayOfWeek) }}
|
||||
@@ -265,7 +265,7 @@
|
||||
</div>
|
||||
<div class="transaction-footer">
|
||||
<span>{{ getDisplayTime(transaction) }}</span>
|
||||
<span v-if="transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ `(${getDisplayTimezone(transaction)})` }}</span>
|
||||
<span v-if="!isSameAsDefaultTimezoneOffsetMinutes(transaction)">{{ `(${getDisplayTimezone(transaction)})` }}</span>
|
||||
<span v-if="transaction.sourceAccount">·</span>
|
||||
<span v-if="transaction.sourceAccount">{{ transaction.sourceAccount.name }}</span>
|
||||
<f7-icon class="transaction-account-arrow icon-with-direction" f7="arrow_right" v-if="transaction.sourceAccount && transaction.type === TransactionType.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id"></f7-icon>
|
||||
@@ -624,7 +624,7 @@ import {
|
||||
DateRangeScene,
|
||||
DateRange
|
||||
} from '@/core/datetime.ts';
|
||||
import { AmountFilterType } from '@/core/numeral.ts';
|
||||
import { type NumeralSystem, AmountFilterType } from '@/core/numeral.ts';
|
||||
import { TransactionType } from '@/core/transaction.ts';
|
||||
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
||||
import { type Transaction, TransactionTagFilter } from '@/models/transaction.ts';
|
||||
@@ -637,8 +637,6 @@ import {
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
parseDateTimeFromUnixTime,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getActualUnixTimeForStore,
|
||||
getDayFirstUnixTimeBySpecifiedUnixTime,
|
||||
getYearMonthFirstUnixTime,
|
||||
getYearMonthLastUnixTime,
|
||||
@@ -664,8 +662,8 @@ const props = defineProps<{
|
||||
const {
|
||||
tt,
|
||||
getCurrentLanguageTextDirection,
|
||||
getWeekdayShortName,
|
||||
getCalendarDisplayDayOfMonthFromUnixTime
|
||||
getCurrentNumeralSystemType,
|
||||
getWeekdayShortName
|
||||
} = useI18n();
|
||||
|
||||
const { showAlert, showToast, routeBackOnError } = useI18nUIComponents();
|
||||
@@ -676,7 +674,6 @@ const {
|
||||
customMinDatetime,
|
||||
customMaxDatetime,
|
||||
currentCalendarDate,
|
||||
currentTimezoneOffsetMinutes,
|
||||
firstDayOfWeek,
|
||||
fiscalYearStart,
|
||||
defaultCurrency,
|
||||
@@ -711,6 +708,7 @@ const {
|
||||
transactionCalendarMaxDate,
|
||||
currentMonthTransactionData,
|
||||
hasSubCategoryInQuery,
|
||||
isSameAsDefaultTimezoneOffsetMinutes,
|
||||
canAddTransaction,
|
||||
getDisplayTime,
|
||||
getDisplayLongYearMonth,
|
||||
@@ -737,6 +735,7 @@ const showCustomMonthSheet = ref<boolean>(false);
|
||||
const showDeleteActionSheet = ref<boolean>(false);
|
||||
|
||||
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
|
||||
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
|
||||
const isDarkMode = computed<boolean>(() => environmentsStore.framework7DarkMode || false);
|
||||
|
||||
const transactions = computed<TransactionMonthList[]>(() => {
|
||||
@@ -1057,7 +1056,7 @@ function changePageType(type: number): void {
|
||||
function changeDateFilter(dateType: number): void {
|
||||
if (dateType === DateRange.Custom.type) { // Custom
|
||||
if (!query.value.minTime || !query.value.maxTime) {
|
||||
customMaxDatetime.value = getActualUnixTimeForStore(getCurrentUnixTime(), currentTimezoneOffsetMinutes.value, getBrowserTimezoneOffsetMinutes());
|
||||
customMaxDatetime.value = getCurrentUnixTime();
|
||||
customMinDatetime.value = getDayFirstUnixTimeBySpecifiedUnixTime(customMaxDatetime.value);
|
||||
} else {
|
||||
customMaxDatetime.value = query.value.maxTime;
|
||||
|
||||
@@ -58,6 +58,7 @@ import { TextDirection } from '@/core/text.ts';
|
||||
import { type TokenInfoResponse, SessionDeviceType, SessionInfo } from '@/models/token.ts';
|
||||
|
||||
import { isEquals } from '@/lib/common.ts';
|
||||
import { parseDateTimeFromUnixTime } from '@/lib/datetime.ts';
|
||||
import { parseSessionInfo } from '@/lib/session.ts';
|
||||
|
||||
class MobilePageSessionInfo extends SessionInfo {
|
||||
@@ -69,7 +70,7 @@ class MobilePageSessionInfo extends SessionInfo {
|
||||
super(sessionInfo.tokenId, sessionInfo.isCurrent, sessionInfo.deviceType, sessionInfo.deviceInfo, sessionInfo.deviceName, sessionInfo.lastSeen);
|
||||
this.domId = getTokenDomId(sessionInfo.tokenId);
|
||||
this.icon = getTokenIcon(sessionInfo.deviceType);
|
||||
this.lastSeenDateTime = sessionInfo.lastSeen ? formatUnixTimeToLongDateTime(sessionInfo.lastSeen) : '-';
|
||||
this.lastSeenDateTime = sessionInfo.lastSeen ? formatDateTimeToLongDateTime(parseDateTimeFromUnixTime(sessionInfo.lastSeen)) : '-';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +78,12 @@ const props = defineProps<{
|
||||
f7router: Router.Router;
|
||||
}>();
|
||||
|
||||
const { tt, getCurrentLanguageTextDirection, formatUnixTimeToLongDateTime } = useI18n();
|
||||
const {
|
||||
tt,
|
||||
getCurrentLanguageTextDirection,
|
||||
formatDateTimeToLongDateTime
|
||||
} = useI18n();
|
||||
|
||||
const { showConfirm, showToast, routeBackOnError } = useI18nUIComponents();
|
||||
|
||||
const tokensStore = useTokensStore();
|
||||
|
||||
Reference in New Issue
Block a user