From 00f8b6d950da586297aa3e744a549a93801bfa60 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sat, 15 Mar 2025 17:21:30 +0800 Subject: [PATCH] code refactor --- pkg/api/transactions.go | 4 +- ...ipay_transaction_data_csv_file_importer.go | 3 +- .../data_table_transaction_data_exporter.go | 166 +++++ .../data_table_transaction_data_importer.go | 454 ++++++++++++++ .../transaction_data_converter.go | 2 +- .../data_table_transaction_data_converter.go | 578 ------------------ ...t_transaction_data_plain_text_converter.go | 5 +- ...stom_transaction_data_dsv_file_importer.go | 6 +- ..._app_transaction_data_csv_file_importer.go | 3 +- ..._web_transaction_data_xls_file_importer.go | 5 +- ...yiii_transaction_data_csv_file_importer.go | 3 +- .../gnucash_transaction_data_file_importer.go | 4 +- .../iif/iif_transaction_data_file_importer.go | 4 +- .../ofx/ofx_transaction_data_file_importer.go | 4 +- .../qif/qif_transaction_data_file_importer.go | 4 +- pkg/converters/transaction_data_converters.go | 8 +- ..._pay_transaction_data_csv_file_importer.go | 3 +- 17 files changed, 652 insertions(+), 604 deletions(-) create mode 100644 pkg/converters/converter/data_table_transaction_data_exporter.go create mode 100644 pkg/converters/converter/data_table_transaction_data_importer.go rename pkg/converters/{base => converter}/transaction_data_converter.go (98%) delete mode 100644 pkg/converters/datatable/data_table_transaction_data_converter.go diff --git a/pkg/api/transactions.go b/pkg/api/transactions.go index 774e3178..493ace69 100644 --- a/pkg/api/transactions.go +++ b/pkg/api/transactions.go @@ -9,7 +9,7 @@ import ( orderedmap "github.com/wk8/go-ordered-map/v2" "github.com/mayswind/ezbookkeeping/pkg/converters" - baseconverters "github.com/mayswind/ezbookkeeping/pkg/converters/base" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/duplicatechecker" @@ -1138,7 +1138,7 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext) fileType := fileTypes[0] - var dataImporter baseconverters.TransactionDataImporter + var dataImporter converter.TransactionDataImporter if converters.IsCustomDelimiterSeparatedValuesFileType(fileType) { fileEncodings := form.Value["fileEncoding"] diff --git a/pkg/converters/alipay/alipay_transaction_data_csv_file_importer.go b/pkg/converters/alipay/alipay_transaction_data_csv_file_importer.go index 5057100c..f91a21c5 100644 --- a/pkg/converters/alipay/alipay_transaction_data_csv_file_importer.go +++ b/pkg/converters/alipay/alipay_transaction_data_csv_file_importer.go @@ -9,6 +9,7 @@ import ( "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" csvdatatable "github.com/mayswind/ezbookkeeping/pkg/converters/csv" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/core" @@ -78,7 +79,7 @@ func (c *alipayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Contex transactionRowParser := createAlipayTransactionDataRowParser(c.originalColumnNames) transactionDataTable := datatable.CreateNewCommonTransactionDataTable(commonDataTable, alipayTransactionSupportedColumns, transactionRowParser) - dataTableImporter := datatable.CreateNewSimpleImporter(alipayTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(alipayTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/converter/data_table_transaction_data_exporter.go b/pkg/converters/converter/data_table_transaction_data_exporter.go new file mode 100644 index 00000000..c3a74a7d --- /dev/null +++ b/pkg/converters/converter/data_table_transaction_data_exporter.go @@ -0,0 +1,166 @@ +package converter + +import ( + "fmt" + "strings" + "time" + + "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" + "github.com/mayswind/ezbookkeeping/pkg/core" + "github.com/mayswind/ezbookkeeping/pkg/models" + "github.com/mayswind/ezbookkeeping/pkg/utils" +) + +// DataTableTransactionDataExporter defines the structure of plain text data table exporter for transaction data +type DataTableTransactionDataExporter struct { + transactionTypeMapping map[models.TransactionType]string + geoLocationSeparator string + transactionTagSeparator string +} + +// BuildExportedContent writes the exported transaction data to the data table builder +func (c *DataTableTransactionDataExporter) BuildExportedContent(ctx core.Context, dataTableBuilder datatable.TransactionDataTableBuilder, uid int64, transactions []*models.Transaction, accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTagIndexes map[int64][]int64) error { + for i := 0; i < len(transactions); i++ { + transaction := transactions[i] + + if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { + continue + } + + dataRowMap := make(map[datatable.TransactionDataTableColumn]string, 15) + 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_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) + dataRowMap[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = c.getExportedAccountName(dataTableBuilder, transaction.AccountId, accountMap) + dataRowMap[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY] = c.getAccountCurrency(dataTableBuilder, transaction.AccountId, accountMap) + dataRowMap[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(transaction.Amount) + + if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + dataRowMap[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME] = c.getExportedAccountName(dataTableBuilder, transaction.RelatedAccountId, accountMap) + dataRowMap[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY] = c.getAccountCurrency(dataTableBuilder, transaction.RelatedAccountId, accountMap) + dataRowMap[datatable.TRANSACTION_DATA_TABLE_RELATED_AMOUNT] = utils.FormatAmount(transaction.RelatedAccountAmount) + } + + dataRowMap[datatable.TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION] = c.getExportedGeographicLocation(transaction) + dataRowMap[datatable.TRANSACTION_DATA_TABLE_TAGS] = c.getExportedTags(dataTableBuilder, transaction.TransactionId, allTagIndexes, tagMap) + dataRowMap[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = dataTableBuilder.ReplaceDelimiters(transaction.Comment) + + dataTableBuilder.AppendTransaction(dataRowMap) + } + + return nil +} + +func (c *DataTableTransactionDataExporter) getDisplayTransactionTypeName(transactionDbType models.TransactionDbType) string { + transactionType, err := transactionDbType.ToTransactionType() + + if err != nil { + return "" + } + + transactionTypeName, exists := c.transactionTypeMapping[transactionType] + + if !exists { + return "" + } + + return transactionTypeName +} + +func (c *DataTableTransactionDataExporter) getExportedTransactionCategoryName(dataTableBuilder datatable.TransactionDataTableBuilder, categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { + category, exists := categoryMap[categoryId] + + if !exists { + return "" + } + + if category.ParentCategoryId == 0 { + return dataTableBuilder.ReplaceDelimiters(category.Name) + } + + parentCategory, exists := categoryMap[category.ParentCategoryId] + + if !exists { + return "" + } + + return dataTableBuilder.ReplaceDelimiters(parentCategory.Name) +} + +func (c *DataTableTransactionDataExporter) getExportedTransactionSubCategoryName(dataTableBuilder datatable.TransactionDataTableBuilder, categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { + category, exists := categoryMap[categoryId] + + if exists { + return dataTableBuilder.ReplaceDelimiters(category.Name) + } else { + return "" + } +} + +func (c *DataTableTransactionDataExporter) getExportedAccountName(dataTableBuilder datatable.TransactionDataTableBuilder, accountId int64, accountMap map[int64]*models.Account) string { + account, exists := accountMap[accountId] + + if exists { + return dataTableBuilder.ReplaceDelimiters(account.Name) + } else { + return "" + } +} + +func (c *DataTableTransactionDataExporter) getAccountCurrency(dataTableBuilder datatable.TransactionDataTableBuilder, accountId int64, accountMap map[int64]*models.Account) string { + account, exists := accountMap[accountId] + + if exists { + return dataTableBuilder.ReplaceDelimiters(account.Currency) + } else { + return "" + } +} + +func (c *DataTableTransactionDataExporter) getExportedGeographicLocation(transaction *models.Transaction) string { + if transaction.GeoLongitude != 0 || transaction.GeoLatitude != 0 { + return fmt.Sprintf("%f%s%f", transaction.GeoLongitude, c.geoLocationSeparator, transaction.GeoLatitude) + } + + return "" +} + +func (c *DataTableTransactionDataExporter) getExportedTags(dataTableBuilder datatable.TransactionDataTableBuilder, transactionId int64, allTagIndexes map[int64][]int64, tagMap map[int64]*models.TransactionTag) string { + tagIndexes, exists := allTagIndexes[transactionId] + + if !exists { + return "" + } + + var ret strings.Builder + + for i := 0; i < len(tagIndexes); i++ { + tagIndex := tagIndexes[i] + tag, exists := tagMap[tagIndex] + + if !exists { + continue + } + + if ret.Len() > 0 { + ret.WriteString(c.transactionTagSeparator) + } + + ret.WriteString(strings.Replace(tag.Name, c.transactionTagSeparator, " ", -1)) + } + + return dataTableBuilder.ReplaceDelimiters(ret.String()) +} + +// CreateNewExporter returns a new data table transaction data exporter according to the specified arguments +func CreateNewExporter(transactionTypeMapping map[models.TransactionType]string, geoLocationSeparator string, transactionTagSeparator string) *DataTableTransactionDataExporter { + return &DataTableTransactionDataExporter{ + transactionTypeMapping: transactionTypeMapping, + geoLocationSeparator: geoLocationSeparator, + transactionTagSeparator: transactionTagSeparator, + } +} diff --git a/pkg/converters/converter/data_table_transaction_data_importer.go b/pkg/converters/converter/data_table_transaction_data_importer.go new file mode 100644 index 00000000..387fe397 --- /dev/null +++ b/pkg/converters/converter/data_table_transaction_data_importer.go @@ -0,0 +1,454 @@ +package converter + +import ( + "sort" + "strings" + + "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" + "github.com/mayswind/ezbookkeeping/pkg/core" + "github.com/mayswind/ezbookkeeping/pkg/errs" + "github.com/mayswind/ezbookkeeping/pkg/log" + "github.com/mayswind/ezbookkeeping/pkg/models" + "github.com/mayswind/ezbookkeeping/pkg/utils" + "github.com/mayswind/ezbookkeeping/pkg/validators" +) + +// DataTableTransactionDataImporter defines the structure of plain text data table importer for transaction data +type DataTableTransactionDataImporter struct { + transactionTypeMapping map[string]models.TransactionType + geoLocationSeparator string + transactionTagSeparator string +} + +// ParseImportedData returns the imported transaction data +func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, dataTable datatable.TransactionDataTable, defaultTimezoneOffset int16, 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.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) { + if dataTable.TransactionRowCount() < 1 { + log.Errorf(ctx, "[data_table_transaction_data_exporter.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 + } + + nameDbTypeMap, err := c.buildTransactionTypeNameDbTypeMap() + + if err != nil { + return nil, nil, nil, nil, nil, nil, err + } + + if !dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME) || + !dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE) || + !dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY) || + !dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME) || + !dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_AMOUNT) || + !dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME) { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse import data for user \"uid:%d\", because missing essential columns in header row", user.Uid) + return nil, nil, nil, nil, nil, nil, errs.ErrMissingRequiredFieldInHeaderRow + } + + if accountMap == nil { + accountMap = make(map[string]*models.Account) + } + + if expenseCategoryMap == nil { + expenseCategoryMap = make(map[string]*models.TransactionCategory) + } + + if incomeCategoryMap == nil { + incomeCategoryMap = make(map[string]*models.TransactionCategory) + } + + if transferCategoryMap == nil { + transferCategoryMap = make(map[string]*models.TransactionCategory) + } + + if tagMap == nil { + tagMap = make(map[string]*models.TransactionTag) + } + + allNewTransactions := make(models.ImportedTransactionSlice, 0, dataTable.TransactionRowCount()) + allNewAccounts := make([]*models.Account, 0) + allNewSubExpenseCategories := make([]*models.TransactionCategory, 0) + allNewSubIncomeCategories := make([]*models.TransactionCategory, 0) + allNewSubTransferCategories := make([]*models.TransactionCategory, 0) + allNewTags := make([]*models.TransactionTag, 0) + + dataRowIterator := dataTable.TransactionRowIterator() + dataRowIndex := 0 + + for dataRowIterator.HasNext() { + dataRowIndex++ + dataRow, err := dataRowIterator.Next(ctx, user) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse data row \"index:%d\" for user \"uid:%d\", because %s", dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, err + } + + if !dataRow.IsValid() { + continue + } + + timezoneOffset := defaultTimezoneOffset + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) { + transactionTimezone, err := utils.ParseFromTimezoneOffset(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE)) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse time zone \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE), dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, errs.ErrTransactionTimeZoneInvalid + } + + timezoneOffset = utils.GetTimezoneOffsetMinutes(transactionTimezone) + } + + transactionTime, err := utils.ParseFromLongDateTime(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), timezoneOffset) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.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()) + return nil, nil, nil, nil, nil, nil, errs.ErrTransactionTimeInvalid + } + + transactionDbType, err := c.getTransactionDbType(nameDbTypeMap, dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE)) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse transaction type \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE), dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, errs.Or(err, errs.ErrTransactionTypeInvalid) + } + + categoryId := int64(0) + subCategoryName := "" + + if transactionDbType != models.TRANSACTION_DB_TYPE_MODIFY_BALANCE { + transactionCategoryType, err := c.getTransactionCategoryType(transactionDbType) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse transaction category type in data row \"index:%d\" for user \"uid:%d\", because %s", dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, errs.Or(err, errs.ErrTransactionTypeInvalid) + } + + subCategoryName = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY) + + if transactionDbType == models.TRANSACTION_DB_TYPE_EXPENSE { + subCategory, exists := expenseCategoryMap[subCategoryName] + + if !exists { + subCategory = c.createNewTransactionCategoryModel(user.Uid, subCategoryName, transactionCategoryType) + allNewSubExpenseCategories = append(allNewSubExpenseCategories, subCategory) + expenseCategoryMap[subCategoryName] = subCategory + } + + categoryId = subCategory.CategoryId + } else if transactionDbType == models.TRANSACTION_DB_TYPE_INCOME { + subCategory, exists := incomeCategoryMap[subCategoryName] + + if !exists { + subCategory = c.createNewTransactionCategoryModel(user.Uid, subCategoryName, transactionCategoryType) + allNewSubIncomeCategories = append(allNewSubIncomeCategories, subCategory) + incomeCategoryMap[subCategoryName] = subCategory + } + + categoryId = subCategory.CategoryId + } else if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + subCategory, exists := transferCategoryMap[subCategoryName] + + if !exists { + subCategory = c.createNewTransactionCategoryModel(user.Uid, subCategoryName, transactionCategoryType) + allNewSubTransferCategories = append(allNewSubTransferCategories, subCategory) + transferCategoryMap[subCategoryName] = subCategory + } + + categoryId = subCategory.CategoryId + } + } + + accountName := dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME) + accountCurrency := user.DefaultCurrency + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY) { + accountCurrency = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY) + + if _, ok := validators.AllCurrencyNames[accountCurrency]; !ok { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] account currency \"%s\" is not supported in data row \"index:%d\" for user \"uid:%d\"", accountCurrency, dataRowIndex, user.Uid) + return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid + } + } + + account, exists := accountMap[accountName] + + if !exists { + account = c.createNewAccountModel(user.Uid, accountName, accountCurrency) + allNewAccounts = append(allNewAccounts, account) + accountMap[accountName] = account + } + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY) { + if account.Name != "" && account.Currency != accountCurrency { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] currency \"%s\" in data row \"index:%d\" not equals currency \"%s\" of the account for user \"uid:%d\"", accountCurrency, dataRowIndex, account.Currency, user.Uid) + return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid + } + } else if exists { + accountCurrency = account.Currency + } + + amount, err := utils.ParseAmount(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_AMOUNT)) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse acmount \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_AMOUNT), dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, errs.ErrAmountInvalid + } + + relatedAccountId := int64(0) + relatedAccountAmount := int64(0) + account2Name := "" + account2Currency := "" + + if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + account2Name = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME) + account2Currency = user.DefaultCurrency + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY) { + account2Currency = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY) + + if _, ok := validators.AllCurrencyNames[account2Currency]; !ok { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] account2 currency \"%s\" is not supported in data row \"index:%d\" for user \"uid:%d\"", account2Currency, dataRowIndex, user.Uid) + return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid + } + } + + account2, exists := accountMap[account2Name] + + if !exists { + account2 = c.createNewAccountModel(user.Uid, account2Name, account2Currency) + allNewAccounts = append(allNewAccounts, account2) + accountMap[account2Name] = account2 + } + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY) { + if account2.Name != "" && account2.Currency != account2Currency { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] currency \"%s\" in data row \"index:%d\" not equals currency \"%s\" of the account2 for user \"uid:%d\"", account2Currency, dataRowIndex, account2.Currency, user.Uid) + return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid + } + } else if exists { + account2Currency = account2.Currency + } + + relatedAccountId = account2.AccountId + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_RELATED_AMOUNT) { + relatedAccountAmount, err = utils.ParseAmount(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_RELATED_AMOUNT)) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse acmount2 \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_RELATED_AMOUNT), dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, errs.ErrAmountInvalid + } + } else if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + relatedAccountAmount = amount + } + } + + geoLongitude := float64(0) + geoLatitude := float64(0) + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION) && c.geoLocationSeparator != "" { + geoLocationItems := strings.Split(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION), c.geoLocationSeparator) + + if len(geoLocationItems) == 2 { + geoLongitude, err = utils.StringToFloat64(geoLocationItems[0]) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse geographic location \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION), dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, errs.ErrGeographicLocationInvalid + } + + geoLatitude, err = utils.StringToFloat64(geoLocationItems[1]) + + if err != nil { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] cannot parse geographic location \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION), dataRowIndex, user.Uid, err.Error()) + return nil, nil, nil, nil, nil, nil, errs.ErrGeographicLocationInvalid + } + } + } + + var tagIds []string + var tagNames []string + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TAGS) { + var tagNameItems []string + + if c.transactionTagSeparator != "" { + tagNameItems = strings.Split(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TAGS), c.transactionTagSeparator) + } else { + tagNameItems = append(tagNameItems, dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TAGS)) + } + + for i := 0; i < len(tagNameItems); i++ { + tagName := tagNameItems[i] + + if tagName == "" { + continue + } + + tag, exists := tagMap[tagName] + + if !exists { + tag = c.createNewTransactionTagModel(user.Uid, tagName) + allNewTags = append(allNewTags, tag) + tagMap[tagName] = tag + } + + if tag != nil { + tagIds = append(tagIds, utils.Int64ToString(tag.TagId)) + } + + tagNames = append(tagNames, tagName) + } + } + + description := "" + + if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_DESCRIPTION) { + description = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_DESCRIPTION) + } + + transaction := &models.ImportTransaction{ + Transaction: &models.Transaction{ + Uid: user.Uid, + Type: transactionDbType, + CategoryId: categoryId, + TransactionTime: utils.GetMinTransactionTimeFromUnixTime(transactionTime.Unix()), + TimezoneUtcOffset: timezoneOffset, + AccountId: account.AccountId, + Amount: amount, + HideAmount: false, + RelatedAccountId: relatedAccountId, + RelatedAccountAmount: relatedAccountAmount, + Comment: description, + GeoLongitude: geoLongitude, + GeoLatitude: geoLatitude, + CreatedIp: "127.0.0.1", + }, + TagIds: tagIds, + OriginalCategoryName: subCategoryName, + OriginalSourceAccountName: accountName, + OriginalSourceAccountCurrency: accountCurrency, + OriginalDestinationAccountName: account2Name, + OriginalDestinationAccountCurrency: account2Currency, + OriginalTagNames: tagNames, + } + + allNewTransactions = append(allNewTransactions, transaction) + } + + if len(allNewTransactions) < 1 { + log.Errorf(ctx, "[data_table_transaction_data_exporter.ParseImportedData] no transaction data parsed for \"uid:%d\"", user.Uid) + return nil, nil, nil, nil, nil, nil, errs.ErrNotFoundTransactionDataInFile + } + + sort.Sort(allNewTransactions) + + return allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, nil +} + +func (c *DataTableTransactionDataImporter) buildTransactionTypeNameDbTypeMap() (map[string]models.TransactionDbType, error) { + if c.transactionTypeMapping == nil { + return nil, errs.ErrTransactionTypeInvalid + } + + nameDbTypeMap := make(map[string]models.TransactionDbType, len(c.transactionTypeMapping)) + + for name, transactionType := range c.transactionTypeMapping { + if transactionType == models.TRANSACTION_TYPE_MODIFY_BALANCE { + nameDbTypeMap[name] = models.TRANSACTION_DB_TYPE_MODIFY_BALANCE + } else if transactionType == models.TRANSACTION_TYPE_INCOME { + nameDbTypeMap[name] = models.TRANSACTION_DB_TYPE_INCOME + } else if transactionType == models.TRANSACTION_TYPE_EXPENSE { + nameDbTypeMap[name] = models.TRANSACTION_DB_TYPE_EXPENSE + } else if transactionType == models.TRANSACTION_TYPE_TRANSFER { + nameDbTypeMap[name] = models.TRANSACTION_DB_TYPE_TRANSFER_OUT + } else { + return nil, errs.ErrTransactionTypeInvalid + } + } + + return nameDbTypeMap, nil +} + +func (c *DataTableTransactionDataImporter) getTransactionDbType(nameDbTypeMap map[string]models.TransactionDbType, transactionTypeName string) (models.TransactionDbType, error) { + transactionType, exists := nameDbTypeMap[transactionTypeName] + + if !exists { + return 0, errs.ErrTransactionTypeInvalid + } + + return transactionType, nil +} + +func (c *DataTableTransactionDataImporter) getTransactionCategoryType(transactionType models.TransactionDbType) (models.TransactionCategoryType, error) { + if transactionType == models.TRANSACTION_DB_TYPE_INCOME { + return models.CATEGORY_TYPE_INCOME, nil + } else if transactionType == models.TRANSACTION_DB_TYPE_EXPENSE { + return models.CATEGORY_TYPE_EXPENSE, nil + } else if transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + return models.CATEGORY_TYPE_TRANSFER, nil + } else { + return 0, errs.ErrTransactionTypeInvalid + } +} + +func (c *DataTableTransactionDataImporter) createNewAccountModel(uid int64, accountName string, currency string) *models.Account { + return &models.Account{ + Uid: uid, + Name: accountName, + Currency: currency, + } +} + +func (c *DataTableTransactionDataImporter) createNewTransactionCategoryModel(uid int64, categoryName string, transactionCategoryType models.TransactionCategoryType) *models.TransactionCategory { + return &models.TransactionCategory{ + Uid: uid, + Name: categoryName, + Type: transactionCategoryType, + } +} + +func (c *DataTableTransactionDataImporter) createNewTransactionTagModel(uid int64, tagName string) *models.TransactionTag { + return &models.TransactionTag{ + Uid: uid, + Name: tagName, + } +} + +// CreateNewImporterWithTypeNameMapping returns a new data table transaction data importer according to the specified arguments +func CreateNewImporterWithTypeNameMapping(transactionTypeMapping map[models.TransactionType]string, geoLocationSeparator string, transactionTagSeparator string) *DataTableTransactionDataImporter { + return &DataTableTransactionDataImporter{ + transactionTypeMapping: buildTransactionNameTypeMap(transactionTypeMapping), + geoLocationSeparator: geoLocationSeparator, + transactionTagSeparator: transactionTagSeparator, + } +} + +// CreateNewSimpleImporter returns a new data table transaction data importer according to the specified arguments +func CreateNewSimpleImporter(transactionTypeMapping map[string]models.TransactionType) *DataTableTransactionDataImporter { + return &DataTableTransactionDataImporter{ + transactionTypeMapping: transactionTypeMapping, + } +} + +// CreateNewSimpleImporterWithTypeNameMapping returns a new data table transaction data importer according to the specified arguments +func CreateNewSimpleImporterWithTypeNameMapping(transactionTypeMapping map[models.TransactionType]string) *DataTableTransactionDataImporter { + return &DataTableTransactionDataImporter{ + transactionTypeMapping: buildTransactionNameTypeMap(transactionTypeMapping), + } +} + +func buildTransactionNameTypeMap(transactionTypeMapping map[models.TransactionType]string) map[string]models.TransactionType { + if transactionTypeMapping == nil { + return nil + } + + typeNameMap := make(map[string]models.TransactionType, len(transactionTypeMapping)) + + for transactionType, name := range transactionTypeMapping { + typeNameMap[name] = transactionType + } + + return typeNameMap +} diff --git a/pkg/converters/base/transaction_data_converter.go b/pkg/converters/converter/transaction_data_converter.go similarity index 98% rename from pkg/converters/base/transaction_data_converter.go rename to pkg/converters/converter/transaction_data_converter.go index ece463dd..3e3f4ae1 100644 --- a/pkg/converters/base/transaction_data_converter.go +++ b/pkg/converters/converter/transaction_data_converter.go @@ -1,4 +1,4 @@ -package base +package converter import ( "github.com/mayswind/ezbookkeeping/pkg/core" diff --git a/pkg/converters/datatable/data_table_transaction_data_converter.go b/pkg/converters/datatable/data_table_transaction_data_converter.go deleted file mode 100644 index 8c90458d..00000000 --- a/pkg/converters/datatable/data_table_transaction_data_converter.go +++ /dev/null @@ -1,578 +0,0 @@ -package datatable - -import ( - "fmt" - "sort" - "strings" - "time" - - "github.com/mayswind/ezbookkeeping/pkg/core" - "github.com/mayswind/ezbookkeeping/pkg/errs" - "github.com/mayswind/ezbookkeeping/pkg/log" - "github.com/mayswind/ezbookkeeping/pkg/models" - "github.com/mayswind/ezbookkeeping/pkg/utils" - "github.com/mayswind/ezbookkeeping/pkg/validators" -) - -// DataTableTransactionDataExporter defines the structure of plain text data table exporter for transaction data -type DataTableTransactionDataExporter struct { - transactionTypeMapping map[models.TransactionType]string - geoLocationSeparator string - transactionTagSeparator string -} - -// DataTableTransactionDataImporter defines the structure of plain text data table importer for transaction data -type DataTableTransactionDataImporter struct { - transactionTypeMapping map[models.TransactionType]string - geoLocationSeparator string - transactionTagSeparator string -} - -// CreateNewExporter returns a new data table transaction data exporter according to the specified arguments -func CreateNewExporter(transactionTypeMapping map[models.TransactionType]string, geoLocationSeparator string, transactionTagSeparator string) *DataTableTransactionDataExporter { - return &DataTableTransactionDataExporter{ - transactionTypeMapping: transactionTypeMapping, - geoLocationSeparator: geoLocationSeparator, - transactionTagSeparator: transactionTagSeparator, - } -} - -// CreateNewImporter returns a new data table transaction data importer according to the specified arguments -func CreateNewImporter(transactionTypeMapping map[models.TransactionType]string, geoLocationSeparator string, transactionTagSeparator string) *DataTableTransactionDataImporter { - return &DataTableTransactionDataImporter{ - transactionTypeMapping: transactionTypeMapping, - geoLocationSeparator: geoLocationSeparator, - transactionTagSeparator: transactionTagSeparator, - } -} - -// CreateNewSimpleImporter returns a new data table transaction data importer according to the specified arguments -func CreateNewSimpleImporter(transactionTypeMapping map[models.TransactionType]string) *DataTableTransactionDataImporter { - return &DataTableTransactionDataImporter{ - transactionTypeMapping: transactionTypeMapping, - } -} - -// BuildExportedContent writes the exported transaction data to the data table builder -func (c *DataTableTransactionDataExporter) BuildExportedContent(ctx core.Context, dataTableBuilder TransactionDataTableBuilder, uid int64, transactions []*models.Transaction, accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTagIndexes map[int64][]int64) error { - for i := 0; i < len(transactions); i++ { - transaction := transactions[i] - - if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - continue - } - - dataRowMap := make(map[TransactionDataTableColumn]string, 15) - transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60) - - dataRowMap[TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime), transactionTimeZone) - dataRowMap[TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(transactionTimeZone) - dataRowMap[TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = dataTableBuilder.ReplaceDelimiters(c.getDisplayTransactionTypeName(transaction.Type)) - dataRowMap[TRANSACTION_DATA_TABLE_CATEGORY] = c.getExportedTransactionCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap) - dataRowMap[TRANSACTION_DATA_TABLE_SUB_CATEGORY] = c.getExportedTransactionSubCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap) - dataRowMap[TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = c.getExportedAccountName(dataTableBuilder, transaction.AccountId, accountMap) - dataRowMap[TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY] = c.getAccountCurrency(dataTableBuilder, transaction.AccountId, accountMap) - dataRowMap[TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(transaction.Amount) - - if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - dataRowMap[TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME] = c.getExportedAccountName(dataTableBuilder, transaction.RelatedAccountId, accountMap) - dataRowMap[TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY] = c.getAccountCurrency(dataTableBuilder, transaction.RelatedAccountId, accountMap) - dataRowMap[TRANSACTION_DATA_TABLE_RELATED_AMOUNT] = utils.FormatAmount(transaction.RelatedAccountAmount) - } - - dataRowMap[TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION] = c.getExportedGeographicLocation(transaction) - dataRowMap[TRANSACTION_DATA_TABLE_TAGS] = c.getExportedTags(dataTableBuilder, transaction.TransactionId, allTagIndexes, tagMap) - dataRowMap[TRANSACTION_DATA_TABLE_DESCRIPTION] = dataTableBuilder.ReplaceDelimiters(transaction.Comment) - - dataTableBuilder.AppendTransaction(dataRowMap) - } - - return nil -} - -func (c *DataTableTransactionDataExporter) getDisplayTransactionTypeName(transactionDbType models.TransactionDbType) string { - transactionType, err := transactionDbType.ToTransactionType() - - if err != nil { - return "" - } - - transactionTypeName, exists := c.transactionTypeMapping[transactionType] - - if !exists { - return "" - } - - return transactionTypeName -} - -func (c *DataTableTransactionDataExporter) getExportedTransactionCategoryName(dataTableBuilder TransactionDataTableBuilder, categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { - category, exists := categoryMap[categoryId] - - if !exists { - return "" - } - - if category.ParentCategoryId == 0 { - return dataTableBuilder.ReplaceDelimiters(category.Name) - } - - parentCategory, exists := categoryMap[category.ParentCategoryId] - - if !exists { - return "" - } - - return dataTableBuilder.ReplaceDelimiters(parentCategory.Name) -} - -func (c *DataTableTransactionDataExporter) getExportedTransactionSubCategoryName(dataTableBuilder TransactionDataTableBuilder, categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { - category, exists := categoryMap[categoryId] - - if exists { - return dataTableBuilder.ReplaceDelimiters(category.Name) - } else { - return "" - } -} - -func (c *DataTableTransactionDataExporter) getExportedAccountName(dataTableBuilder TransactionDataTableBuilder, accountId int64, accountMap map[int64]*models.Account) string { - account, exists := accountMap[accountId] - - if exists { - return dataTableBuilder.ReplaceDelimiters(account.Name) - } else { - return "" - } -} - -func (c *DataTableTransactionDataExporter) getAccountCurrency(dataTableBuilder TransactionDataTableBuilder, accountId int64, accountMap map[int64]*models.Account) string { - account, exists := accountMap[accountId] - - if exists { - return dataTableBuilder.ReplaceDelimiters(account.Currency) - } else { - return "" - } -} - -func (c *DataTableTransactionDataExporter) getExportedGeographicLocation(transaction *models.Transaction) string { - if transaction.GeoLongitude != 0 || transaction.GeoLatitude != 0 { - return fmt.Sprintf("%f%s%f", transaction.GeoLongitude, c.geoLocationSeparator, transaction.GeoLatitude) - } - - return "" -} - -func (c *DataTableTransactionDataExporter) getExportedTags(dataTableBuilder TransactionDataTableBuilder, transactionId int64, allTagIndexes map[int64][]int64, tagMap map[int64]*models.TransactionTag) string { - tagIndexes, exists := allTagIndexes[transactionId] - - if !exists { - return "" - } - - var ret strings.Builder - - for i := 0; i < len(tagIndexes); i++ { - tagIndex := tagIndexes[i] - tag, exists := tagMap[tagIndex] - - if !exists { - continue - } - - if ret.Len() > 0 { - ret.WriteString(c.transactionTagSeparator) - } - - ret.WriteString(strings.Replace(tag.Name, c.transactionTagSeparator, " ", -1)) - } - - return dataTableBuilder.ReplaceDelimiters(ret.String()) -} - -// ParseImportedData returns the imported transaction data -func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, dataTable TransactionDataTable, defaultTimezoneOffset int16, 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.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) { - if dataTable.TransactionRowCount() < 1 { - log.Errorf(ctx, "[data_table_transaction_data_converter.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 - } - - nameDbTypeMap, err := c.buildTransactionTypeNameDbTypeMap() - - if err != nil { - return nil, nil, nil, nil, nil, nil, err - } - - if !dataTable.HasColumn(TRANSACTION_DATA_TABLE_TRANSACTION_TIME) || - !dataTable.HasColumn(TRANSACTION_DATA_TABLE_TRANSACTION_TYPE) || - !dataTable.HasColumn(TRANSACTION_DATA_TABLE_SUB_CATEGORY) || - !dataTable.HasColumn(TRANSACTION_DATA_TABLE_ACCOUNT_NAME) || - !dataTable.HasColumn(TRANSACTION_DATA_TABLE_AMOUNT) || - !dataTable.HasColumn(TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME) { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse import data for user \"uid:%d\", because missing essential columns in header row", user.Uid) - return nil, nil, nil, nil, nil, nil, errs.ErrMissingRequiredFieldInHeaderRow - } - - if accountMap == nil { - accountMap = make(map[string]*models.Account) - } - - if expenseCategoryMap == nil { - expenseCategoryMap = make(map[string]*models.TransactionCategory) - } - - if incomeCategoryMap == nil { - incomeCategoryMap = make(map[string]*models.TransactionCategory) - } - - if transferCategoryMap == nil { - transferCategoryMap = make(map[string]*models.TransactionCategory) - } - - if tagMap == nil { - tagMap = make(map[string]*models.TransactionTag) - } - - allNewTransactions := make(models.ImportedTransactionSlice, 0, dataTable.TransactionRowCount()) - allNewAccounts := make([]*models.Account, 0) - allNewSubExpenseCategories := make([]*models.TransactionCategory, 0) - allNewSubIncomeCategories := make([]*models.TransactionCategory, 0) - allNewSubTransferCategories := make([]*models.TransactionCategory, 0) - allNewTags := make([]*models.TransactionTag, 0) - - dataRowIterator := dataTable.TransactionRowIterator() - dataRowIndex := 0 - - for dataRowIterator.HasNext() { - dataRowIndex++ - dataRow, err := dataRowIterator.Next(ctx, user) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse data row \"index:%d\" for user \"uid:%d\", because %s", dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, err - } - - if !dataRow.IsValid() { - continue - } - - timezoneOffset := defaultTimezoneOffset - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) { - transactionTimezone, err := utils.ParseFromTimezoneOffset(dataRow.GetData(TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE)) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse time zone \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE), dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.ErrTransactionTimeZoneInvalid - } - - timezoneOffset = utils.GetTimezoneOffsetMinutes(transactionTimezone) - } - - transactionTime, err := utils.ParseFromLongDateTime(dataRow.GetData(TRANSACTION_DATA_TABLE_TRANSACTION_TIME), timezoneOffset) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse time \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(TRANSACTION_DATA_TABLE_TRANSACTION_TIME), dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.ErrTransactionTimeInvalid - } - - transactionDbType, err := c.getTransactionDbType(nameDbTypeMap, dataRow.GetData(TRANSACTION_DATA_TABLE_TRANSACTION_TYPE)) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse transaction type \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(TRANSACTION_DATA_TABLE_TRANSACTION_TYPE), dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.Or(err, errs.ErrTransactionTypeInvalid) - } - - categoryId := int64(0) - subCategoryName := "" - - if transactionDbType != models.TRANSACTION_DB_TYPE_MODIFY_BALANCE { - transactionCategoryType, err := c.getTransactionCategoryType(transactionDbType) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse transaction category type in data row \"index:%d\" for user \"uid:%d\", because %s", dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.Or(err, errs.ErrTransactionTypeInvalid) - } - - subCategoryName = dataRow.GetData(TRANSACTION_DATA_TABLE_SUB_CATEGORY) - - if transactionDbType == models.TRANSACTION_DB_TYPE_EXPENSE { - subCategory, exists := expenseCategoryMap[subCategoryName] - - if !exists { - subCategory = c.createNewTransactionCategoryModel(user.Uid, subCategoryName, transactionCategoryType) - allNewSubExpenseCategories = append(allNewSubExpenseCategories, subCategory) - expenseCategoryMap[subCategoryName] = subCategory - } - - categoryId = subCategory.CategoryId - } else if transactionDbType == models.TRANSACTION_DB_TYPE_INCOME { - subCategory, exists := incomeCategoryMap[subCategoryName] - - if !exists { - subCategory = c.createNewTransactionCategoryModel(user.Uid, subCategoryName, transactionCategoryType) - allNewSubIncomeCategories = append(allNewSubIncomeCategories, subCategory) - incomeCategoryMap[subCategoryName] = subCategory - } - - categoryId = subCategory.CategoryId - } else if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - subCategory, exists := transferCategoryMap[subCategoryName] - - if !exists { - subCategory = c.createNewTransactionCategoryModel(user.Uid, subCategoryName, transactionCategoryType) - allNewSubTransferCategories = append(allNewSubTransferCategories, subCategory) - transferCategoryMap[subCategoryName] = subCategory - } - - categoryId = subCategory.CategoryId - } - } - - accountName := dataRow.GetData(TRANSACTION_DATA_TABLE_ACCOUNT_NAME) - accountCurrency := user.DefaultCurrency - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY) { - accountCurrency = dataRow.GetData(TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY) - - if _, ok := validators.AllCurrencyNames[accountCurrency]; !ok { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] account currency \"%s\" is not supported in data row \"index:%d\" for user \"uid:%d\"", accountCurrency, dataRowIndex, user.Uid) - return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid - } - } - - account, exists := accountMap[accountName] - - if !exists { - account = c.createNewAccountModel(user.Uid, accountName, accountCurrency) - allNewAccounts = append(allNewAccounts, account) - accountMap[accountName] = account - } - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY) { - if account.Name != "" && account.Currency != accountCurrency { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] currency \"%s\" in data row \"index:%d\" not equals currency \"%s\" of the account for user \"uid:%d\"", accountCurrency, dataRowIndex, account.Currency, user.Uid) - return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid - } - } else if exists { - accountCurrency = account.Currency - } - - amount, err := utils.ParseAmount(dataRow.GetData(TRANSACTION_DATA_TABLE_AMOUNT)) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse acmount \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(TRANSACTION_DATA_TABLE_AMOUNT), dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.ErrAmountInvalid - } - - relatedAccountId := int64(0) - relatedAccountAmount := int64(0) - account2Name := "" - account2Currency := "" - - if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - account2Name = dataRow.GetData(TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME) - account2Currency = user.DefaultCurrency - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY) { - account2Currency = dataRow.GetData(TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY) - - if _, ok := validators.AllCurrencyNames[account2Currency]; !ok { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] account2 currency \"%s\" is not supported in data row \"index:%d\" for user \"uid:%d\"", account2Currency, dataRowIndex, user.Uid) - return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid - } - } - - account2, exists := accountMap[account2Name] - - if !exists { - account2 = c.createNewAccountModel(user.Uid, account2Name, account2Currency) - allNewAccounts = append(allNewAccounts, account2) - accountMap[account2Name] = account2 - } - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY) { - if account2.Name != "" && account2.Currency != account2Currency { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] currency \"%s\" in data row \"index:%d\" not equals currency \"%s\" of the account2 for user \"uid:%d\"", account2Currency, dataRowIndex, account2.Currency, user.Uid) - return nil, nil, nil, nil, nil, nil, errs.ErrAccountCurrencyInvalid - } - } else if exists { - account2Currency = account2.Currency - } - - relatedAccountId = account2.AccountId - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_RELATED_AMOUNT) { - relatedAccountAmount, err = utils.ParseAmount(dataRow.GetData(TRANSACTION_DATA_TABLE_RELATED_AMOUNT)) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse acmount2 \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(TRANSACTION_DATA_TABLE_RELATED_AMOUNT), dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.ErrAmountInvalid - } - } else if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - relatedAccountAmount = amount - } - } - - geoLongitude := float64(0) - geoLatitude := float64(0) - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION) && c.geoLocationSeparator != "" { - geoLocationItems := strings.Split(dataRow.GetData(TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION), c.geoLocationSeparator) - - if len(geoLocationItems) == 2 { - geoLongitude, err = utils.StringToFloat64(geoLocationItems[0]) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse geographic location \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION), dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.ErrGeographicLocationInvalid - } - - geoLatitude, err = utils.StringToFloat64(geoLocationItems[1]) - - if err != nil { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] cannot parse geographic location \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION), dataRowIndex, user.Uid, err.Error()) - return nil, nil, nil, nil, nil, nil, errs.ErrGeographicLocationInvalid - } - } - } - - var tagIds []string - var tagNames []string - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_TAGS) { - var tagNameItems []string - - if c.transactionTagSeparator != "" { - tagNameItems = strings.Split(dataRow.GetData(TRANSACTION_DATA_TABLE_TAGS), c.transactionTagSeparator) - } else { - tagNameItems = append(tagNameItems, dataRow.GetData(TRANSACTION_DATA_TABLE_TAGS)) - } - - for i := 0; i < len(tagNameItems); i++ { - tagName := tagNameItems[i] - - if tagName == "" { - continue - } - - tag, exists := tagMap[tagName] - - if !exists { - tag = c.createNewTransactionTagModel(user.Uid, tagName) - allNewTags = append(allNewTags, tag) - tagMap[tagName] = tag - } - - if tag != nil { - tagIds = append(tagIds, utils.Int64ToString(tag.TagId)) - } - - tagNames = append(tagNames, tagName) - } - } - - description := "" - - if dataTable.HasColumn(TRANSACTION_DATA_TABLE_DESCRIPTION) { - description = dataRow.GetData(TRANSACTION_DATA_TABLE_DESCRIPTION) - } - - transaction := &models.ImportTransaction{ - Transaction: &models.Transaction{ - Uid: user.Uid, - Type: transactionDbType, - CategoryId: categoryId, - TransactionTime: utils.GetMinTransactionTimeFromUnixTime(transactionTime.Unix()), - TimezoneUtcOffset: timezoneOffset, - AccountId: account.AccountId, - Amount: amount, - HideAmount: false, - RelatedAccountId: relatedAccountId, - RelatedAccountAmount: relatedAccountAmount, - Comment: description, - GeoLongitude: geoLongitude, - GeoLatitude: geoLatitude, - CreatedIp: "127.0.0.1", - }, - TagIds: tagIds, - OriginalCategoryName: subCategoryName, - OriginalSourceAccountName: accountName, - OriginalSourceAccountCurrency: accountCurrency, - OriginalDestinationAccountName: account2Name, - OriginalDestinationAccountCurrency: account2Currency, - OriginalTagNames: tagNames, - } - - allNewTransactions = append(allNewTransactions, transaction) - } - - if len(allNewTransactions) < 1 { - log.Errorf(ctx, "[data_table_transaction_data_converter.parseImportedData] no transaction data parsed for \"uid:%d\"", user.Uid) - return nil, nil, nil, nil, nil, nil, errs.ErrNotFoundTransactionDataInFile - } - - sort.Sort(allNewTransactions) - - return allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, nil -} - -func (c *DataTableTransactionDataImporter) buildTransactionTypeNameDbTypeMap() (map[string]models.TransactionDbType, error) { - if c.transactionTypeMapping == nil { - return nil, errs.ErrTransactionTypeInvalid - } - - nameDbTypeMap := make(map[string]models.TransactionDbType, len(c.transactionTypeMapping)) - nameDbTypeMap[c.transactionTypeMapping[models.TRANSACTION_TYPE_MODIFY_BALANCE]] = models.TRANSACTION_DB_TYPE_MODIFY_BALANCE - nameDbTypeMap[c.transactionTypeMapping[models.TRANSACTION_TYPE_INCOME]] = models.TRANSACTION_DB_TYPE_INCOME - nameDbTypeMap[c.transactionTypeMapping[models.TRANSACTION_TYPE_EXPENSE]] = models.TRANSACTION_DB_TYPE_EXPENSE - nameDbTypeMap[c.transactionTypeMapping[models.TRANSACTION_TYPE_TRANSFER]] = models.TRANSACTION_DB_TYPE_TRANSFER_OUT - - return nameDbTypeMap, nil -} - -func (c *DataTableTransactionDataImporter) getTransactionDbType(nameDbTypeMap map[string]models.TransactionDbType, transactionTypeName string) (models.TransactionDbType, error) { - transactionType, exists := nameDbTypeMap[transactionTypeName] - - if !exists { - return 0, errs.ErrTransactionTypeInvalid - } - - return transactionType, nil -} - -func (c *DataTableTransactionDataImporter) getTransactionCategoryType(transactionType models.TransactionDbType) (models.TransactionCategoryType, error) { - if transactionType == models.TRANSACTION_DB_TYPE_INCOME { - return models.CATEGORY_TYPE_INCOME, nil - } else if transactionType == models.TRANSACTION_DB_TYPE_EXPENSE { - return models.CATEGORY_TYPE_EXPENSE, nil - } else if transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - return models.CATEGORY_TYPE_TRANSFER, nil - } else { - return 0, errs.ErrTransactionTypeInvalid - } -} - -func (c *DataTableTransactionDataImporter) createNewAccountModel(uid int64, accountName string, currency string) *models.Account { - return &models.Account{ - Uid: uid, - Name: accountName, - Currency: currency, - } -} - -func (c *DataTableTransactionDataImporter) createNewTransactionCategoryModel(uid int64, categoryName string, transactionCategoryType models.TransactionCategoryType) *models.TransactionCategory { - return &models.TransactionCategory{ - Uid: uid, - Name: categoryName, - Type: transactionCategoryType, - } -} - -func (c *DataTableTransactionDataImporter) createNewTransactionTagModel(uid int64, tagName string) *models.TransactionTag { - return &models.TransactionTag{ - Uid: uid, - Name: tagName, - } -} diff --git a/pkg/converters/default/default_transaction_data_plain_text_converter.go b/pkg/converters/default/default_transaction_data_plain_text_converter.go index 2d97a966..a2275eda 100644 --- a/pkg/converters/default/default_transaction_data_plain_text_converter.go +++ b/pkg/converters/default/default_transaction_data_plain_text_converter.go @@ -1,6 +1,7 @@ package _default import ( + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/models" @@ -66,7 +67,7 @@ func (c *defaultTransactionDataPlainTextConverter) ToExportedContent(ctx core.Co ezbookkeepingLineSeparator, ) - dataTableExporter := datatable.CreateNewExporter( + dataTableExporter := converter.CreateNewExporter( ezbookkeepingTransactionTypeNameMapping, ezbookkeepingGeoLocationSeparator, ezbookkeepingTagSeparator, @@ -95,7 +96,7 @@ func (c *defaultTransactionDataPlainTextConverter) ParseImportedData(ctx core.Co transactionDataTable := datatable.CreateNewImportedTransactionDataTable(dataTable, ezbookkeepingDataColumnNameMapping) - dataTableImporter := datatable.CreateNewImporter( + dataTableImporter := converter.CreateNewImporterWithTypeNameMapping( ezbookkeepingTransactionTypeNameMapping, ezbookkeepingGeoLocationSeparator, ezbookkeepingTagSeparator, diff --git a/pkg/converters/dsv/custom_transaction_data_dsv_file_importer.go b/pkg/converters/dsv/custom_transaction_data_dsv_file_importer.go index c899210b..db67fcc8 100644 --- a/pkg/converters/dsv/custom_transaction_data_dsv_file_importer.go +++ b/pkg/converters/dsv/custom_transaction_data_dsv_file_importer.go @@ -15,7 +15,7 @@ import ( "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" - "github.com/mayswind/ezbookkeeping/pkg/converters/base" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" csvconverter "github.com/mayswind/ezbookkeeping/pkg/converters/csv" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/core" @@ -156,7 +156,7 @@ func (c *customTransactionDataDsvFileImporter) ParseImportedData(ctx core.Contex dataTable := csvconverter.CreateNewCustomCsvImportedDataTable(allLines) transactionDataTable := CreateNewCustomPlainTextDataTable(dataTable, c.columnIndexMapping, c.transactionTypeNameMapping, c.timeFormat, c.timezoneFormat) - dataTableImporter := datatable.CreateNewImporter(customTransactionTypeNameMapping, c.geoLocationSeparator, c.transactionTagSeparator) + dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(customTransactionTypeNameMapping, c.geoLocationSeparator, c.transactionTagSeparator) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } @@ -188,7 +188,7 @@ func CreateNewCustomTransactionDataDsvFileParser(fileType string, fileEncoding s } // CreateNewCustomTransactionDataDsvFileImporter returns a new custom dsv importer for transaction data -func CreateNewCustomTransactionDataDsvFileImporter(fileType string, fileEncoding string, columnIndexMapping map[datatable.TransactionDataTableColumn]int, transactionTypeNameMapping map[string]models.TransactionType, hasHeaderLine bool, timeFormat string, timezoneFormat string, geoLocationSeparator string, transactionTagSeparator string) (base.TransactionDataImporter, error) { +func CreateNewCustomTransactionDataDsvFileImporter(fileType string, fileEncoding string, columnIndexMapping map[datatable.TransactionDataTableColumn]int, transactionTypeNameMapping map[string]models.TransactionType, hasHeaderLine bool, timeFormat string, timezoneFormat string, geoLocationSeparator string, transactionTagSeparator string) (converter.TransactionDataImporter, error) { separator, exists := supportedFileTypeSeparators[fileType] if !exists { diff --git a/pkg/converters/feidee/feidee_mymoney_app_transaction_data_csv_file_importer.go b/pkg/converters/feidee/feidee_mymoney_app_transaction_data_csv_file_importer.go index 46518344..489e4b32 100644 --- a/pkg/converters/feidee/feidee_mymoney_app_transaction_data_csv_file_importer.go +++ b/pkg/converters/feidee/feidee_mymoney_app_transaction_data_csv_file_importer.go @@ -8,6 +8,7 @@ import ( "io" "strings" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" csvdatatable "github.com/mayswind/ezbookkeeping/pkg/converters/csv" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/core" @@ -82,7 +83,7 @@ func (c *feideeMymoneyAppTransactionDataCsvFileImporter) ParseImportedData(ctx c return nil, nil, nil, nil, nil, nil, err } - dataTableImporter := datatable.CreateNewSimpleImporter(feideeMymoneyTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/feidee/feidee_mymoney_web_transaction_data_xls_file_importer.go b/pkg/converters/feidee/feidee_mymoney_web_transaction_data_xls_file_importer.go index 2c8160b1..5d6db6aa 100644 --- a/pkg/converters/feidee/feidee_mymoney_web_transaction_data_xls_file_importer.go +++ b/pkg/converters/feidee/feidee_mymoney_web_transaction_data_xls_file_importer.go @@ -1,6 +1,7 @@ package feidee import ( + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/converters/excel" "github.com/mayswind/ezbookkeeping/pkg/core" @@ -20,7 +21,7 @@ var feideeMymoneyWebDataColumnNameMapping = map[datatable.TransactionDataTableCo // feideeMymoneyWebTransactionDataXlsFileImporter defines the structure of feidee mymoney (web) xls importer for transaction data type feideeMymoneyWebTransactionDataXlsFileImporter struct { - datatable.DataTableTransactionDataImporter + converter.DataTableTransactionDataImporter } // Initialize a feidee mymoney (web) transaction data xls file importer singleton instance @@ -38,7 +39,7 @@ func (c *feideeMymoneyWebTransactionDataXlsFileImporter) ParseImportedData(ctx c transactionRowParser := createFeideeMymoneyTransactionDataRowParser() transactionDataTable := datatable.CreateNewImportedTransactionDataTableWithRowParser(dataTable, feideeMymoneyWebDataColumnNameMapping, transactionRowParser) - dataTableImporter := datatable.CreateNewSimpleImporter(feideeMymoneyTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/fireflyIII/fireflyiii_transaction_data_csv_file_importer.go b/pkg/converters/fireflyIII/fireflyiii_transaction_data_csv_file_importer.go index 20dfac0e..3fd81a9d 100644 --- a/pkg/converters/fireflyIII/fireflyiii_transaction_data_csv_file_importer.go +++ b/pkg/converters/fireflyIII/fireflyiii_transaction_data_csv_file_importer.go @@ -3,6 +3,7 @@ package fireflyIII import ( "bytes" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/converters/csv" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/core" @@ -49,7 +50,7 @@ func (c *fireflyIIITransactionDataCsvFileImporter) ParseImportedData(ctx core.Co transactionRowParser := createFireflyIIITransactionDataRowParser() transactionDataTable := datatable.CreateNewImportedTransactionDataTableWithRowParser(dataTable, fireflyIIITransactionDataColumnNameMapping, transactionRowParser) - dataTableImporter := datatable.CreateNewImporter(fireflyIIITransactionTypeNameMapping, "", ",") + dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(fireflyIIITransactionTypeNameMapping, "", ",") return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/gnucash/gnucash_transaction_data_file_importer.go b/pkg/converters/gnucash/gnucash_transaction_data_file_importer.go index fe4aa134..007a3955 100644 --- a/pkg/converters/gnucash/gnucash_transaction_data_file_importer.go +++ b/pkg/converters/gnucash/gnucash_transaction_data_file_importer.go @@ -1,7 +1,7 @@ package gnucash import ( - "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/models" "github.com/mayswind/ezbookkeeping/pkg/utils" @@ -43,7 +43,7 @@ func (c *gnucashTransactionDataImporter) ParseImportedData(ctx core.Context, use return nil, nil, nil, nil, nil, nil, err } - dataTableImporter := datatable.CreateNewSimpleImporter(gnucashTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(gnucashTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/iif/iif_transaction_data_file_importer.go b/pkg/converters/iif/iif_transaction_data_file_importer.go index 061d99ae..73837623 100644 --- a/pkg/converters/iif/iif_transaction_data_file_importer.go +++ b/pkg/converters/iif/iif_transaction_data_file_importer.go @@ -1,7 +1,7 @@ package iif import ( - "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/models" "github.com/mayswind/ezbookkeeping/pkg/utils" @@ -37,7 +37,7 @@ func (c *iifTransactionDataFileImporter) ParseImportedData(ctx core.Context, use return nil, nil, nil, nil, nil, nil, err } - dataTableImporter := datatable.CreateNewSimpleImporter(iifTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(iifTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/ofx/ofx_transaction_data_file_importer.go b/pkg/converters/ofx/ofx_transaction_data_file_importer.go index 74f7485e..149340b6 100644 --- a/pkg/converters/ofx/ofx_transaction_data_file_importer.go +++ b/pkg/converters/ofx/ofx_transaction_data_file_importer.go @@ -1,7 +1,7 @@ package ofx import ( - "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/models" "github.com/mayswind/ezbookkeeping/pkg/utils" @@ -42,7 +42,7 @@ func (c *ofxTransactionDataImporter) ParseImportedData(ctx core.Context, user *m return nil, nil, nil, nil, nil, nil, err } - dataTableImporter := datatable.CreateNewSimpleImporter(ofxTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(ofxTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/qif/qif_transaction_data_file_importer.go b/pkg/converters/qif/qif_transaction_data_file_importer.go index b429791d..ffdd1472 100644 --- a/pkg/converters/qif/qif_transaction_data_file_importer.go +++ b/pkg/converters/qif/qif_transaction_data_file_importer.go @@ -1,7 +1,7 @@ package qif import ( - "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/models" "github.com/mayswind/ezbookkeeping/pkg/utils" @@ -49,7 +49,7 @@ func (c *qifTransactionDataImporter) ParseImportedData(ctx core.Context, user *m return nil, nil, nil, nil, nil, nil, err } - dataTableImporter := datatable.CreateNewSimpleImporter(qifTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(qifTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) } diff --git a/pkg/converters/transaction_data_converters.go b/pkg/converters/transaction_data_converters.go index e6171fae..1ba60b8c 100644 --- a/pkg/converters/transaction_data_converters.go +++ b/pkg/converters/transaction_data_converters.go @@ -2,7 +2,7 @@ package converters import ( "github.com/mayswind/ezbookkeeping/pkg/converters/alipay" - "github.com/mayswind/ezbookkeeping/pkg/converters/base" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/converters/default" "github.com/mayswind/ezbookkeeping/pkg/converters/dsv" @@ -18,7 +18,7 @@ import ( ) // GetTransactionDataExporter returns the transaction data exporter according to the file type -func GetTransactionDataExporter(fileType string) base.TransactionDataExporter { +func GetTransactionDataExporter(fileType string) converter.TransactionDataExporter { if fileType == "csv" { return _default.DefaultTransactionDataCSVFileConverter } else if fileType == "tsv" { @@ -29,7 +29,7 @@ func GetTransactionDataExporter(fileType string) base.TransactionDataExporter { } // GetTransactionDataImporter returns the transaction data importer according to the file type -func GetTransactionDataImporter(fileType string) (base.TransactionDataImporter, error) { +func GetTransactionDataImporter(fileType string) (converter.TransactionDataImporter, error) { if fileType == "ezbookkeeping_csv" { return _default.DefaultTransactionDataCSVFileConverter, nil } else if fileType == "ezbookkeeping_tsv" { @@ -76,6 +76,6 @@ func CreateNewDelimiterSeparatedValuesDataParser(fileType string, fileEncoding s } // CreateNewDelimiterSeparatedValuesDataImporter returns a new delimiter-separated values data importer according to the file type and encoding -func CreateNewDelimiterSeparatedValuesDataImporter(fileType string, fileEncoding string, columnIndexMapping map[datatable.TransactionDataTableColumn]int, transactionTypeNameMapping map[string]models.TransactionType, hasHeaderLine bool, timeFormat string, timezoneFormat string, geoLocationSeparator string, transactionTagSeparator string) (base.TransactionDataImporter, error) { +func CreateNewDelimiterSeparatedValuesDataImporter(fileType string, fileEncoding string, columnIndexMapping map[datatable.TransactionDataTableColumn]int, transactionTypeNameMapping map[string]models.TransactionType, hasHeaderLine bool, timeFormat string, timezoneFormat string, geoLocationSeparator string, transactionTagSeparator string) (converter.TransactionDataImporter, error) { return dsv.CreateNewCustomTransactionDataDsvFileImporter(fileType, fileEncoding, columnIndexMapping, transactionTypeNameMapping, hasHeaderLine, timeFormat, timezoneFormat, geoLocationSeparator, transactionTagSeparator) } diff --git a/pkg/converters/wechat/wechat_pay_transaction_data_csv_file_importer.go b/pkg/converters/wechat/wechat_pay_transaction_data_csv_file_importer.go index 026cef6a..96d433a7 100644 --- a/pkg/converters/wechat/wechat_pay_transaction_data_csv_file_importer.go +++ b/pkg/converters/wechat/wechat_pay_transaction_data_csv_file_importer.go @@ -8,6 +8,7 @@ import ( "io" "strings" + "github.com/mayswind/ezbookkeeping/pkg/converters/converter" csvdatatable "github.com/mayswind/ezbookkeeping/pkg/converters/csv" "github.com/mayswind/ezbookkeeping/pkg/converters/datatable" "github.com/mayswind/ezbookkeeping/pkg/core" @@ -67,7 +68,7 @@ func (c *wechatPayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Con transactionRowParser := createWeChatPayTransactionDataRowParser() transactionDataTable := datatable.CreateNewCommonTransactionDataTable(commonDataTable, wechatPayTransactionSupportedColumns, transactionRowParser) - dataTableImporter := datatable.CreateNewSimpleImporter(wechatPayTransactionTypeNameMapping) + dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(wechatPayTransactionTypeNameMapping) return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap) }