Files
ezbookkeeping/pkg/converters/mt/mt_transaction_data_table.go
T

179 lines
6.6 KiB
Go

package mt
import (
"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"
)
var mt940TransactionSupportedColumns = map[datatable.TransactionDataTableColumn]bool{
datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME: true,
datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE: true,
datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY: true,
datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME: true,
datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY: true,
datatable.TRANSACTION_DATA_TABLE_AMOUNT: true,
datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME: true,
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: true,
}
// mt940TransactionDataTable represents the mt940 statement data dataTable
type mt940TransactionDataTable struct {
data *mt940Data
}
// mt940TransactionDataRow represents a row in the mt940 statement data dataTable
type mt940TransactionDataRow struct {
statement *mtStatement
finalItems map[datatable.TransactionDataTableColumn]string
}
// mt940TransactionDataRowIterator represents an iterator for mt940 statement data rows
type mt940TransactionDataRowIterator struct {
dataTable *mt940TransactionDataTable
currentIndex int
}
// HasColumn implements TransactionDataTable.HasColumn
func (t *mt940TransactionDataTable) HasColumn(column datatable.TransactionDataTableColumn) bool {
_, exists := mt940TransactionSupportedColumns[column]
return exists
}
// TransactionRowCount implements TransactionDataTable.TransactionRowCount
func (t *mt940TransactionDataTable) TransactionRowCount() int {
return len(t.data.Statements)
}
// TransactionRowIterator implements TransactionDataTable.TransactionRowIterator
func (t *mt940TransactionDataTable) TransactionRowIterator() datatable.TransactionDataRowIterator {
return &mt940TransactionDataRowIterator{
dataTable: t,
currentIndex: -1,
}
}
// IsValid implements TransactionDataRow.IsValid
func (r *mt940TransactionDataRow) IsValid() bool {
return true
}
// GetData implements TransactionDataRow.GetData
func (r *mt940TransactionDataRow) GetData(column datatable.TransactionDataTableColumn) string {
_, exists := mt940TransactionSupportedColumns[column]
if exists {
return r.finalItems[column]
}
return ""
}
// HasNext implements TransactionDataRowIterator.HasNext
func (t *mt940TransactionDataRowIterator) HasNext() bool {
return t.currentIndex+1 < len(t.dataTable.data.Statements)
}
// Next implements TransactionDataRowIterator.Next
func (t *mt940TransactionDataRowIterator) Next(ctx core.Context, user *models.User) (datatable.TransactionDataRow, error) {
if t.currentIndex+1 >= len(t.dataTable.data.Statements) {
return nil, nil
}
t.currentIndex++
data := t.dataTable.data.Statements[t.currentIndex]
rowItems, err := t.parseTransaction(ctx, user, t.dataTable.data, data)
if err != nil {
log.Errorf(ctx, "[mt_transaction_data_table.Next] cannot parsing transaction in row#%d, because %s", t.currentIndex, err.Error())
return nil, err
}
return &mt940TransactionDataRow{
statement: data,
finalItems: rowItems,
}, nil
}
func (t *mt940TransactionDataRowIterator) parseTransaction(ctx core.Context, user *models.User, mt940Data *mt940Data, statement *mtStatement) (map[datatable.TransactionDataTableColumn]string, error) {
data := make(map[datatable.TransactionDataTableColumn]string, len(mt940TransactionSupportedColumns))
if statement.ValueDate == "" && len(statement.ValueDate) != 6 {
return nil, errs.ErrTransactionTimeInvalid
}
transactionTime, err := utils.FormatYearMonthDayToLongDateTime(statement.ValueDate[0:2], statement.ValueDate[2:4], statement.ValueDate[4:6])
if err != nil {
log.Errorf(ctx, "[mt_transaction_data_table.parseTransaction] cannot format transaction time in row#%d, because %s", t.currentIndex, err.Error())
return nil, errs.ErrTransactionTimeInvalid
}
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = transactionTime
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = mt940Data.AccountId
if mt940Data.OpeningBalance != nil && mt940Data.OpeningBalance.Currency != "" {
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY] = mt940Data.OpeningBalance.Currency
} else if mt940Data.ClosingBalance != nil && mt940Data.ClosingBalance.Currency != "" {
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY] = mt940Data.ClosingBalance.Currency
} else {
return nil, errs.ErrAccountCurrencyInvalid
}
amountValue := strings.ReplaceAll(statement.Amount, ",", ".") // decimal separator is comma in mt data
if len(amountValue) > 0 && amountValue[len(amountValue)-1] == '.' {
amountValue = amountValue[:len(amountValue)-1]
}
amount, err := utils.ParseAmount(amountValue)
if err != nil {
log.Errorf(ctx, "[mt_transaction_data_table.parseTransaction] cannot parsing transaction amount \"%s\", because %s", statement.Amount, err.Error())
return nil, errs.ErrAmountInvalid
}
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(amount)
if statement.CreditDebitMark == MT_MARK_CREDIT {
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = utils.IntToString(int(models.TRANSACTION_TYPE_INCOME))
} else if statement.CreditDebitMark == MT_MARK_DEBIT {
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = utils.IntToString(int(models.TRANSACTION_TYPE_EXPENSE))
} else if statement.CreditDebitMark == MT_MARK_REVERSAL_CREDIT {
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = utils.IntToString(int(models.TRANSACTION_TYPE_EXPENSE))
} else if statement.CreditDebitMark == MT_MARK_REVERSAL_DEBIT {
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = utils.IntToString(int(models.TRANSACTION_TYPE_INCOME))
} else {
return nil, errs.ErrTransactionTypeInvalid
}
informationToAccountOwnerMap := statement.GetInformationToAccountOwnerMap()
if len(informationToAccountOwnerMap) > 0 {
if value, exists := informationToAccountOwnerMap[MT_INFORMATION_TO_ACCOUNT_OWNER_TAG_REMITTANCE]; exists {
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = value
}
} else {
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = strings.Join(statement.InformationToAccountOwner, "\n")
}
return data, nil
}
// createNewMT940TransactionDataTable creates a new mt940 statement data dataTable
func createNewMT940TransactionDataTable(data *mt940Data) (*mt940TransactionDataTable, error) {
if data == nil || len(data.Statements) < 1 {
return nil, errs.ErrNotFoundTransactionDataInFile
}
return &mt940TransactionDataTable{
data: data,
}, nil
}