mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 01:04:25 +08:00
import transactions from mt940 file
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = strings.Join(statement.AdditionalInformation, "\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
|
||||
}
|
||||
Reference in New Issue
Block a user