code refactor
This commit is contained in:
@@ -41,49 +41,49 @@ const (
|
||||
|
||||
// beancountData defines the structure of beancount data
|
||||
type beancountData struct {
|
||||
accounts map[string]*beancountAccount
|
||||
transactions []*beancountTransactionEntry
|
||||
Accounts map[string]*beancountAccount
|
||||
Transactions []*beancountTransactionEntry
|
||||
}
|
||||
|
||||
// beancountAccount defines the structure of beancount account
|
||||
type beancountAccount struct {
|
||||
name string
|
||||
accountType beancountAccountType
|
||||
openDate string
|
||||
closeDate string
|
||||
Name string
|
||||
AccountType beancountAccountType
|
||||
OpenDate string
|
||||
CloseDate string
|
||||
}
|
||||
|
||||
// beancountTransactionEntry defines the structure of beancount transaction entry
|
||||
type beancountTransactionEntry struct {
|
||||
date string
|
||||
directive beancountDirective
|
||||
payee string
|
||||
narration string
|
||||
postings []*beancountPosting
|
||||
tags []string
|
||||
links []string
|
||||
metadata map[string]string
|
||||
Date string
|
||||
Directive beancountDirective
|
||||
Payee string
|
||||
Narration string
|
||||
Postings []*beancountPosting
|
||||
Tags []string
|
||||
Links []string
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
// beancountPosting defines the structure of beancount transaction posting
|
||||
type beancountPosting struct {
|
||||
account string
|
||||
amount string
|
||||
originalAmount string
|
||||
commodity string
|
||||
totalCost string
|
||||
totalCostCommodity string
|
||||
price string
|
||||
priceCommodity string
|
||||
metadata map[string]string
|
||||
Account string
|
||||
Amount string
|
||||
OriginalAmount string
|
||||
Commodity string
|
||||
TotalCost string
|
||||
TotalCostCommodity string
|
||||
Price string
|
||||
PriceCommodity string
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
func (a *beancountAccount) isOpeningBalanceEquityAccount() bool {
|
||||
if a.accountType != beancountEquityAccountType {
|
||||
if a.AccountType != beancountEquityAccountType {
|
||||
return false
|
||||
}
|
||||
|
||||
nameItems := strings.Split(a.name, string(beancountMetadataKeySuffix))
|
||||
nameItems := strings.Split(a.Name, string(beancountMetadataKeySuffix))
|
||||
|
||||
if len(nameItems) != 2 {
|
||||
return false
|
||||
|
||||
@@ -49,8 +49,8 @@ func (r *beancountDataReader) read(ctx core.Context) (*beancountData, error) {
|
||||
}
|
||||
|
||||
data := &beancountData{
|
||||
accounts: make(map[string]*beancountAccount),
|
||||
transactions: make([]*beancountTransactionEntry, 0),
|
||||
Accounts: make(map[string]*beancountAccount),
|
||||
Transactions: make([]*beancountTransactionEntry, 0),
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -100,7 +100,7 @@ func (r *beancountDataReader) read(ctx core.Context) (*beancountData, error) {
|
||||
|
||||
if ('A' <= actualFirstItem[0] && actualFirstItem[0] <= 'Z') || actualFirstItem[0] == '!' { // transaction posting
|
||||
if currentTransactionEntry != nil && currentTransactionPosting != nil {
|
||||
currentTransactionEntry.postings = append(currentTransactionEntry.postings, currentTransactionPosting)
|
||||
currentTransactionEntry.Postings = append(currentTransactionEntry.Postings, currentTransactionPosting)
|
||||
currentTransactionPosting = nil
|
||||
}
|
||||
|
||||
@@ -120,12 +120,12 @@ func (r *beancountDataReader) read(ctx core.Context) (*beancountData, error) {
|
||||
metadataValue := metadata[1]
|
||||
|
||||
if currentTransactionPosting != nil {
|
||||
if _, exists := currentTransactionPosting.metadata[metadataKey]; !exists {
|
||||
currentTransactionPosting.metadata[metadataKey] = metadataValue
|
||||
if _, exists := currentTransactionPosting.Metadata[metadataKey]; !exists {
|
||||
currentTransactionPosting.Metadata[metadataKey] = metadataValue
|
||||
}
|
||||
} else if currentTransactionEntry != nil {
|
||||
if _, exists := currentTransactionEntry.metadata[metadataKey]; !exists {
|
||||
currentTransactionEntry.metadata[metadataKey] = metadataValue
|
||||
if _, exists := currentTransactionEntry.Metadata[metadataKey]; !exists {
|
||||
currentTransactionEntry.Metadata[metadataKey] = metadataValue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -172,11 +172,11 @@ func (r *beancountDataReader) read(ctx core.Context) (*beancountData, error) {
|
||||
|
||||
if currentTransactionEntry != nil {
|
||||
if currentTransactionPosting != nil {
|
||||
currentTransactionEntry.postings = append(currentTransactionEntry.postings, currentTransactionPosting)
|
||||
currentTransactionEntry.Postings = append(currentTransactionEntry.Postings, currentTransactionPosting)
|
||||
currentTransactionPosting = nil
|
||||
}
|
||||
|
||||
data.transactions = append(data.transactions, currentTransactionEntry)
|
||||
data.Transactions = append(data.Transactions, currentTransactionEntry)
|
||||
currentTransactionEntry = nil
|
||||
}
|
||||
|
||||
@@ -186,11 +186,11 @@ func (r *beancountDataReader) read(ctx core.Context) (*beancountData, error) {
|
||||
func (r *beancountDataReader) updateCurrentState(data *beancountData, currentTransactionEntry *beancountTransactionEntry, currentTransactionPosting *beancountPosting) (*beancountTransactionEntry, *beancountPosting) {
|
||||
if currentTransactionEntry != nil {
|
||||
if currentTransactionPosting != nil {
|
||||
currentTransactionEntry.postings = append(currentTransactionEntry.postings, currentTransactionPosting)
|
||||
currentTransactionEntry.Postings = append(currentTransactionEntry.Postings, currentTransactionPosting)
|
||||
currentTransactionPosting = nil
|
||||
}
|
||||
|
||||
data.transactions = append(data.transactions, currentTransactionEntry)
|
||||
data.Transactions = append(data.Transactions, currentTransactionEntry)
|
||||
currentTransactionEntry = nil
|
||||
currentTransactionPosting = nil
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func (r *beancountDataReader) readAccountLine(ctx core.Context, lineIndex int, i
|
||||
|
||||
var err error
|
||||
accountName := r.getNotEmptyItemByIndex(items, 2)
|
||||
account, exists := data.accounts[accountName]
|
||||
account, exists := data.Accounts[accountName]
|
||||
|
||||
if !exists {
|
||||
account, err = r.createAccount(ctx, data, accountName)
|
||||
@@ -288,10 +288,10 @@ func (r *beancountDataReader) readAccountLine(ctx core.Context, lineIndex int, i
|
||||
}
|
||||
|
||||
if directive == beancountDirectiveOpen {
|
||||
account.openDate = date
|
||||
account.OpenDate = date
|
||||
return account, nil
|
||||
} else if directive == beancountDirectiveClose {
|
||||
account.closeDate = date
|
||||
account.CloseDate = date
|
||||
return account, nil
|
||||
} else {
|
||||
log.Warnf(ctx, "[beancount_data_reader.parseAccount] cannot parse account line#%d \"%s\", because directive is invalid", lineIndex, strings.Join(items, " "))
|
||||
@@ -301,8 +301,8 @@ func (r *beancountDataReader) readAccountLine(ctx core.Context, lineIndex int, i
|
||||
|
||||
func (r *beancountDataReader) createAccount(ctx core.Context, data *beancountData, accountName string) (*beancountAccount, error) {
|
||||
account := &beancountAccount{
|
||||
name: accountName,
|
||||
accountType: beancountUnknownAccountType,
|
||||
Name: accountName,
|
||||
AccountType: beancountUnknownAccountType,
|
||||
}
|
||||
|
||||
accountNameItems := strings.Split(accountName, beancountAccountNameItemsSeparator)
|
||||
@@ -311,31 +311,31 @@ func (r *beancountDataReader) createAccount(ctx core.Context, data *beancountDat
|
||||
accountType, exists := r.accountTypeNameMap[accountNameItems[0]]
|
||||
|
||||
if exists {
|
||||
account.accountType = accountType
|
||||
account.AccountType = accountType
|
||||
} else {
|
||||
log.Warnf(ctx, "[beancount_data_reader.createAccount] cannot parse account \"%s\", because account type \"%s\" is invalid", accountName, accountNameItems[0])
|
||||
return nil, errs.ErrInvalidBeancountFile
|
||||
}
|
||||
}
|
||||
|
||||
data.accounts[accountName] = account
|
||||
data.Accounts[accountName] = account
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (r *beancountDataReader) readTransactionLine(ctx core.Context, lineIndex int, items []string, date string, directive beancountDirective, tags []string) *beancountTransactionEntry {
|
||||
transactionEntry := &beancountTransactionEntry{
|
||||
date: date,
|
||||
directive: directive,
|
||||
tags: make([]string, 0),
|
||||
links: make([]string, 0),
|
||||
metadata: make(map[string]string),
|
||||
Date: date,
|
||||
Directive: directive,
|
||||
Tags: make([]string, 0),
|
||||
Links: make([]string, 0),
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
|
||||
transactionEntry.tags = append(transactionEntry.tags, tags...)
|
||||
transactionEntry.Tags = append(transactionEntry.Tags, tags...)
|
||||
|
||||
allTags := make(map[string]bool, len(transactionEntry.tags))
|
||||
allTags := make(map[string]bool, len(transactionEntry.Tags))
|
||||
|
||||
for _, tag := range transactionEntry.tags {
|
||||
for _, tag := range transactionEntry.Tags {
|
||||
allTags[tag] = true
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ func (r *beancountDataReader) readTransactionLine(ctx core.Context, lineIndex in
|
||||
tagName := item[1:]
|
||||
|
||||
if _, exists := allTags[tagName]; !exists {
|
||||
transactionEntry.tags = append(transactionEntry.tags, tagName)
|
||||
transactionEntry.Tags = append(transactionEntry.Tags, tagName)
|
||||
allTags[tagName] = true
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ func (r *beancountDataReader) readTransactionLine(ctx core.Context, lineIndex in
|
||||
payeeNarrationLastIndex = i - 1
|
||||
}
|
||||
} else if item[0] == beancountLinkPrefix { // [ˆlink]
|
||||
transactionEntry.links = append(transactionEntry.links, item[1:])
|
||||
transactionEntry.Links = append(transactionEntry.Links, item[1:])
|
||||
|
||||
if i-1 < payeeNarrationLastIndex {
|
||||
payeeNarrationLastIndex = i - 1
|
||||
@@ -380,10 +380,10 @@ func (r *beancountDataReader) readTransactionLine(ctx core.Context, lineIndex in
|
||||
}
|
||||
|
||||
if payeeNarrationLastIndex-payeeNarrationFirstIndex >= 1 {
|
||||
transactionEntry.payee = items[payeeNarrationFirstIndex]
|
||||
transactionEntry.narration = items[payeeNarrationFirstIndex+1]
|
||||
transactionEntry.Payee = items[payeeNarrationFirstIndex]
|
||||
transactionEntry.Narration = items[payeeNarrationFirstIndex+1]
|
||||
} else if payeeNarrationLastIndex-payeeNarrationFirstIndex >= 0 {
|
||||
transactionEntry.narration = items[payeeNarrationFirstIndex]
|
||||
transactionEntry.Narration = items[payeeNarrationFirstIndex]
|
||||
}
|
||||
|
||||
return transactionEntry
|
||||
@@ -410,36 +410,36 @@ func (r *beancountDataReader) readTransactionPostingLine(ctx core.Context, lineI
|
||||
}
|
||||
|
||||
transactionPositing := &beancountPosting{
|
||||
account: accountName,
|
||||
metadata: make(map[string]string),
|
||||
Account: accountName,
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
|
||||
amountActualLastIndex := -1
|
||||
transactionPositing.originalAmount, amountActualLastIndex = r.getOriginalAmountAndLastIndexFromIndex(items, accountNameActualIndex+1)
|
||||
transactionPositing.OriginalAmount, amountActualLastIndex = r.getOriginalAmountAndLastIndexFromIndex(items, accountNameActualIndex+1)
|
||||
|
||||
if transactionPositing.originalAmount == "" || amountActualLastIndex < 0 {
|
||||
if transactionPositing.OriginalAmount == "" || amountActualLastIndex < 0 {
|
||||
log.Warnf(ctx, "[beancount_data_reader.readTransactionPostingLine] cannot parse transaction posting line#%d \"%s\", because missing amount", lineIndex, strings.Join(items, " "))
|
||||
return nil, errs.ErrAmountInvalid
|
||||
}
|
||||
|
||||
finalAmount, err := evaluateBeancountAmountExpression(ctx, transactionPositing.originalAmount)
|
||||
finalAmount, err := evaluateBeancountAmountExpression(ctx, transactionPositing.OriginalAmount)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(ctx, "[beancount_data_reader.readTransactionPostingLine] cannot evaluate amount expression in line#%d \"%s\", because %s", lineIndex, strings.Join(items, " "), err.Error())
|
||||
return nil, errs.ErrAmountInvalid
|
||||
} else {
|
||||
transactionPositing.amount = finalAmount
|
||||
transactionPositing.Amount = finalAmount
|
||||
}
|
||||
|
||||
commodityActualIndex := -1
|
||||
transactionPositing.commodity, commodityActualIndex = r.getNotEmptyItemAndIndexFromIndex(items, amountActualLastIndex+1)
|
||||
transactionPositing.Commodity, commodityActualIndex = r.getNotEmptyItemAndIndexFromIndex(items, amountActualLastIndex+1)
|
||||
|
||||
if transactionPositing.commodity == "" || commodityActualIndex < 0 {
|
||||
if transactionPositing.Commodity == "" || commodityActualIndex < 0 {
|
||||
log.Warnf(ctx, "[beancount_data_reader.readTransactionPostingLine] cannot parse transaction posting line#%d \"%s\", because missing commodity", lineIndex, strings.Join(items, " "))
|
||||
return nil, errs.ErrInvalidBeancountFile
|
||||
}
|
||||
|
||||
if strings.ToUpper(transactionPositing.commodity) != transactionPositing.commodity { // The syntax for a currency is a word all in capital letters
|
||||
if strings.ToUpper(transactionPositing.Commodity) != transactionPositing.Commodity { // The syntax for a currency is a word all in capital letters
|
||||
log.Warnf(ctx, "[beancount_data_reader.readTransactionPostingLine] cannot parse transaction posting line#%d \"%s\", because commodity name is not capital letters", lineIndex, strings.Join(items, " "))
|
||||
return nil, errs.ErrInvalidBeancountFile
|
||||
}
|
||||
@@ -461,13 +461,13 @@ func (r *beancountDataReader) readTransactionPostingLine(ctx core.Context, lineI
|
||||
totalCost, totalCostActualIndex := r.getNotEmptyItemAndIndexFromIndex(items, i+1)
|
||||
|
||||
if totalCostActualIndex > 0 {
|
||||
transactionPositing.totalCost = totalCost
|
||||
transactionPositing.TotalCost = totalCost
|
||||
i = totalCostActualIndex
|
||||
|
||||
totalCostCommodity, totalCostCommodityActualIndex := r.getNotEmptyItemAndIndexFromIndex(items, totalCostActualIndex+1)
|
||||
|
||||
if totalCostCommodityActualIndex > 0 {
|
||||
transactionPositing.totalCostCommodity = totalCostCommodity
|
||||
transactionPositing.TotalCostCommodity = totalCostCommodity
|
||||
i = totalCostCommodityActualIndex
|
||||
}
|
||||
}
|
||||
@@ -475,13 +475,13 @@ func (r *beancountDataReader) readTransactionPostingLine(ctx core.Context, lineI
|
||||
price, priceActualIndex := r.getNotEmptyItemAndIndexFromIndex(items, i+1)
|
||||
|
||||
if priceActualIndex > 0 {
|
||||
transactionPositing.price = price
|
||||
transactionPositing.Price = price
|
||||
i = priceActualIndex
|
||||
|
||||
priceCommodity, priceCommodityActualIndex := r.getNotEmptyItemAndIndexFromIndex(items, priceActualIndex+1)
|
||||
|
||||
if priceCommodityActualIndex > 0 {
|
||||
transactionPositing.priceCommodity = priceCommodity
|
||||
transactionPositing.PriceCommodity = priceCommodity
|
||||
i = priceCommodityActualIndex
|
||||
}
|
||||
}
|
||||
@@ -489,11 +489,11 @@ func (r *beancountDataReader) readTransactionPostingLine(ctx core.Context, lineI
|
||||
}
|
||||
}
|
||||
|
||||
if transactionPositing.account != "" {
|
||||
_, exists := data.accounts[transactionPositing.account]
|
||||
if transactionPositing.Account != "" {
|
||||
_, exists := data.Accounts[transactionPositing.Account]
|
||||
|
||||
if !exists {
|
||||
_, err := r.createAccount(ctx, data, transactionPositing.account)
|
||||
_, err := r.createAccount(ctx, data, transactionPositing.Account)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -41,56 +41,56 @@ func TestBeancountDataReaderRead(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 5, len(actualData.accounts))
|
||||
assert.Equal(t, "AssetsAccount:TestAccount", actualData.accounts["AssetsAccount:TestAccount"].name)
|
||||
assert.Equal(t, beancountAssetsAccountType, actualData.accounts["AssetsAccount:TestAccount"].accountType)
|
||||
assert.Equal(t, "2024-01-01", actualData.accounts["AssetsAccount:TestAccount"].openDate)
|
||||
assert.Equal(t, "2024-01-07", actualData.accounts["AssetsAccount:TestAccount"].closeDate)
|
||||
assert.Equal(t, 5, len(actualData.Accounts))
|
||||
assert.Equal(t, "AssetsAccount:TestAccount", actualData.Accounts["AssetsAccount:TestAccount"].Name)
|
||||
assert.Equal(t, beancountAssetsAccountType, actualData.Accounts["AssetsAccount:TestAccount"].AccountType)
|
||||
assert.Equal(t, "2024-01-01", actualData.Accounts["AssetsAccount:TestAccount"].OpenDate)
|
||||
assert.Equal(t, "2024-01-07", actualData.Accounts["AssetsAccount:TestAccount"].CloseDate)
|
||||
|
||||
assert.Equal(t, "LiabilitiesAccount:TestAccount2", actualData.accounts["LiabilitiesAccount:TestAccount2"].name)
|
||||
assert.Equal(t, beancountLiabilitiesAccountType, actualData.accounts["LiabilitiesAccount:TestAccount2"].accountType)
|
||||
assert.Equal(t, "2024-01-02", actualData.accounts["LiabilitiesAccount:TestAccount2"].openDate)
|
||||
assert.Equal(t, "LiabilitiesAccount:TestAccount2", actualData.Accounts["LiabilitiesAccount:TestAccount2"].Name)
|
||||
assert.Equal(t, beancountLiabilitiesAccountType, actualData.Accounts["LiabilitiesAccount:TestAccount2"].AccountType)
|
||||
assert.Equal(t, "2024-01-02", actualData.Accounts["LiabilitiesAccount:TestAccount2"].OpenDate)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.transactions))
|
||||
assert.Equal(t, 2, len(actualData.Transactions))
|
||||
|
||||
assert.Equal(t, "2024-01-05", actualData.transactions[0].date)
|
||||
assert.Equal(t, "Payee Name", actualData.transactions[0].payee)
|
||||
assert.Equal(t, "Foo Bar", actualData.transactions[0].narration)
|
||||
assert.Equal(t, 2, len(actualData.transactions[0].postings))
|
||||
assert.Equal(t, "IncomeAccount:TestCategory", actualData.transactions[0].postings[0].account)
|
||||
assert.Equal(t, "-123.45", actualData.transactions[0].postings[0].amount)
|
||||
assert.Equal(t, "CNY", actualData.transactions[0].postings[0].commodity)
|
||||
assert.Equal(t, "AssetsAccount:TestAccount", actualData.transactions[0].postings[1].account)
|
||||
assert.Equal(t, "123.45", actualData.transactions[0].postings[1].amount)
|
||||
assert.Equal(t, "CNY", actualData.transactions[0].postings[1].commodity)
|
||||
assert.Equal(t, "2024-01-05", actualData.Transactions[0].Date)
|
||||
assert.Equal(t, "Payee Name", actualData.Transactions[0].Payee)
|
||||
assert.Equal(t, "Foo Bar", actualData.Transactions[0].Narration)
|
||||
assert.Equal(t, 2, len(actualData.Transactions[0].Postings))
|
||||
assert.Equal(t, "IncomeAccount:TestCategory", actualData.Transactions[0].Postings[0].Account)
|
||||
assert.Equal(t, "-123.45", actualData.Transactions[0].Postings[0].Amount)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[0].Postings[0].Commodity)
|
||||
assert.Equal(t, "AssetsAccount:TestAccount", actualData.Transactions[0].Postings[1].Account)
|
||||
assert.Equal(t, "123.45", actualData.Transactions[0].Postings[1].Amount)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[0].Postings[1].Commodity)
|
||||
|
||||
assert.Equal(t, 4, len(actualData.transactions[0].tags))
|
||||
assert.Equal(t, actualData.transactions[0].tags[0], "tag1")
|
||||
assert.Equal(t, actualData.transactions[0].tags[1], "tag2")
|
||||
assert.Equal(t, actualData.transactions[0].tags[2], "tag3")
|
||||
assert.Equal(t, actualData.transactions[0].tags[3], "tag4")
|
||||
assert.Equal(t, 4, len(actualData.Transactions[0].Tags))
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[0], "tag1")
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[1], "tag2")
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[2], "tag3")
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[3], "tag4")
|
||||
|
||||
assert.Equal(t, 1, len(actualData.transactions[0].links))
|
||||
assert.Equal(t, actualData.transactions[0].links[0], "test-link")
|
||||
assert.Equal(t, 1, len(actualData.Transactions[0].Links))
|
||||
assert.Equal(t, actualData.Transactions[0].Links[0], "test-link")
|
||||
|
||||
assert.Equal(t, "2024-01-06", actualData.transactions[1].date)
|
||||
assert.Equal(t, "", actualData.transactions[1].payee)
|
||||
assert.Equal(t, "test\n#test2", actualData.transactions[1].narration)
|
||||
assert.Equal(t, 2, len(actualData.transactions[1].postings))
|
||||
assert.Equal(t, "LiabilitiesAccount:TestAccount2", actualData.transactions[1].postings[0].account)
|
||||
assert.Equal(t, "-0.12", actualData.transactions[1].postings[0].amount)
|
||||
assert.Equal(t, "USD", actualData.transactions[1].postings[0].commodity)
|
||||
assert.Equal(t, "ExpensesAccount:TestCategory2", actualData.transactions[1].postings[1].account)
|
||||
assert.Equal(t, "0.12", actualData.transactions[1].postings[1].amount)
|
||||
assert.Equal(t, "USD", actualData.transactions[1].postings[1].commodity)
|
||||
assert.Equal(t, "2024-01-06", actualData.Transactions[1].Date)
|
||||
assert.Equal(t, "", actualData.Transactions[1].Payee)
|
||||
assert.Equal(t, "test\n#test2", actualData.Transactions[1].Narration)
|
||||
assert.Equal(t, 2, len(actualData.Transactions[1].Postings))
|
||||
assert.Equal(t, "LiabilitiesAccount:TestAccount2", actualData.Transactions[1].Postings[0].Account)
|
||||
assert.Equal(t, "-0.12", actualData.Transactions[1].Postings[0].Amount)
|
||||
assert.Equal(t, "USD", actualData.Transactions[1].Postings[0].Commodity)
|
||||
assert.Equal(t, "ExpensesAccount:TestCategory2", actualData.Transactions[1].Postings[1].Account)
|
||||
assert.Equal(t, "0.12", actualData.Transactions[1].Postings[1].Amount)
|
||||
assert.Equal(t, "USD", actualData.Transactions[1].Postings[1].Commodity)
|
||||
|
||||
assert.Equal(t, 3, len(actualData.transactions[1].tags))
|
||||
assert.Equal(t, actualData.transactions[1].tags[0], "tag2")
|
||||
assert.Equal(t, actualData.transactions[1].tags[1], "tag5")
|
||||
assert.Equal(t, actualData.transactions[1].tags[2], "tag6")
|
||||
assert.Equal(t, 3, len(actualData.Transactions[1].Tags))
|
||||
assert.Equal(t, actualData.Transactions[1].Tags[0], "tag2")
|
||||
assert.Equal(t, actualData.Transactions[1].Tags[1], "tag5")
|
||||
assert.Equal(t, actualData.Transactions[1].Tags[2], "tag6")
|
||||
|
||||
assert.Equal(t, 1, len(actualData.transactions[1].links))
|
||||
assert.Equal(t, actualData.transactions[1].links[0], "test-link2")
|
||||
assert.Equal(t, 1, len(actualData.Transactions[1].Links))
|
||||
assert.Equal(t, actualData.Transactions[1].Links[0], "test-link2")
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderRead_EmptyContent(t *testing.T) {
|
||||
@@ -147,17 +147,17 @@ func TestBeancountDataReaderReadAndSetOption_AccountTypeName(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 3, len(actualData.accounts))
|
||||
assert.Equal(t, 3, len(actualData.Accounts))
|
||||
|
||||
assert.Equal(t, "A:TestAccount", actualData.accounts["A:TestAccount"].name)
|
||||
assert.Equal(t, beancountAssetsAccountType, actualData.accounts["A:TestAccount"].accountType)
|
||||
assert.Equal(t, "A:TestAccount", actualData.Accounts["A:TestAccount"].Name)
|
||||
assert.Equal(t, beancountAssetsAccountType, actualData.Accounts["A:TestAccount"].AccountType)
|
||||
|
||||
assert.Equal(t, "L:TestAccount2", actualData.accounts["L:TestAccount2"].name)
|
||||
assert.Equal(t, beancountLiabilitiesAccountType, actualData.accounts["L:TestAccount2"].accountType)
|
||||
assert.Equal(t, "L:TestAccount2", actualData.Accounts["L:TestAccount2"].Name)
|
||||
assert.Equal(t, beancountLiabilitiesAccountType, actualData.Accounts["L:TestAccount2"].AccountType)
|
||||
|
||||
assert.Equal(t, "E:Opening-Balances", actualData.accounts["E:Opening-Balances"].name)
|
||||
assert.Equal(t, beancountEquityAccountType, actualData.accounts["E:Opening-Balances"].accountType)
|
||||
assert.True(t, actualData.accounts["E:Opening-Balances"].isOpeningBalanceEquityAccount())
|
||||
assert.Equal(t, "E:Opening-Balances", actualData.Accounts["E:Opening-Balances"].Name)
|
||||
assert.Equal(t, beancountEquityAccountType, actualData.Accounts["E:Opening-Balances"].AccountType)
|
||||
assert.True(t, actualData.Accounts["E:Opening-Balances"].isOpeningBalanceEquityAccount())
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderReadAndSetOption_InvalidLineOrUnsupportedOption(t *testing.T) {
|
||||
@@ -203,31 +203,31 @@ func TestBeancountDataReaderReadAndSetTags(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 5, len(actualData.transactions))
|
||||
assert.Equal(t, 5, len(actualData.Transactions))
|
||||
|
||||
assert.Equal(t, 4, len(actualData.transactions[0].tags))
|
||||
assert.Equal(t, actualData.transactions[0].tags[0], "tag1")
|
||||
assert.Equal(t, actualData.transactions[0].tags[1], "tag2")
|
||||
assert.Equal(t, actualData.transactions[0].tags[2], "tag3")
|
||||
assert.Equal(t, actualData.transactions[0].tags[3], "tag4")
|
||||
assert.Equal(t, 4, len(actualData.Transactions[0].Tags))
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[0], "tag1")
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[1], "tag2")
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[2], "tag3")
|
||||
assert.Equal(t, actualData.Transactions[0].Tags[3], "tag4")
|
||||
|
||||
assert.Equal(t, 2, len(actualData.transactions[1].tags))
|
||||
assert.Equal(t, actualData.transactions[1].tags[0], "tag5")
|
||||
assert.Equal(t, actualData.transactions[1].tags[1], "tag6")
|
||||
assert.Equal(t, 2, len(actualData.Transactions[1].Tags))
|
||||
assert.Equal(t, actualData.Transactions[1].Tags[0], "tag5")
|
||||
assert.Equal(t, actualData.Transactions[1].Tags[1], "tag6")
|
||||
|
||||
assert.Equal(t, 2, len(actualData.transactions[2].tags))
|
||||
assert.Equal(t, actualData.transactions[2].tags[0], "tag5")
|
||||
assert.Equal(t, actualData.transactions[2].tags[1], "tag6")
|
||||
assert.Equal(t, 2, len(actualData.Transactions[2].Tags))
|
||||
assert.Equal(t, actualData.Transactions[2].Tags[0], "tag5")
|
||||
assert.Equal(t, actualData.Transactions[2].Tags[1], "tag6")
|
||||
|
||||
assert.Equal(t, 3, len(actualData.transactions[3].tags))
|
||||
assert.Equal(t, actualData.transactions[3].tags[0], "tag3")
|
||||
assert.Equal(t, actualData.transactions[3].tags[1], "tag6")
|
||||
assert.Equal(t, actualData.transactions[3].tags[2], "tag5")
|
||||
assert.Equal(t, 3, len(actualData.Transactions[3].Tags))
|
||||
assert.Equal(t, actualData.Transactions[3].Tags[0], "tag3")
|
||||
assert.Equal(t, actualData.Transactions[3].Tags[1], "tag6")
|
||||
assert.Equal(t, actualData.Transactions[3].Tags[2], "tag5")
|
||||
|
||||
assert.Equal(t, 3, len(actualData.transactions[4].tags))
|
||||
assert.Equal(t, actualData.transactions[4].tags[0], "tag3")
|
||||
assert.Equal(t, actualData.transactions[4].tags[1], "tag6")
|
||||
assert.Equal(t, actualData.transactions[4].tags[2], "tag5")
|
||||
assert.Equal(t, 3, len(actualData.Transactions[4].Tags))
|
||||
assert.Equal(t, actualData.Transactions[4].Tags[0], "tag3")
|
||||
assert.Equal(t, actualData.Transactions[4].Tags[1], "tag6")
|
||||
assert.Equal(t, actualData.Transactions[4].Tags[2], "tag5")
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderReadAccountLine_InvalidLine(t *testing.T) {
|
||||
@@ -238,7 +238,7 @@ func TestBeancountDataReaderReadAccountLine_InvalidLine(t *testing.T) {
|
||||
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, len(actualData.accounts))
|
||||
assert.Equal(t, 0, len(actualData.Accounts))
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderReadAccountLine_InvalidAccountType(t *testing.T) {
|
||||
@@ -274,44 +274,44 @@ func TestBeancountDataReaderReadTransactionLine(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 6, len(actualData.transactions))
|
||||
assert.Equal(t, 6, len(actualData.Transactions))
|
||||
|
||||
assert.Equal(t, "2024-01-01", actualData.transactions[0].date)
|
||||
assert.Equal(t, beancountDirectiveCompletedTransaction, actualData.transactions[0].directive)
|
||||
assert.Equal(t, "", actualData.transactions[0].payee)
|
||||
assert.Equal(t, "", actualData.transactions[0].narration)
|
||||
assert.Equal(t, "2024-01-01", actualData.Transactions[0].Date)
|
||||
assert.Equal(t, beancountDirectiveCompletedTransaction, actualData.Transactions[0].Directive)
|
||||
assert.Equal(t, "", actualData.Transactions[0].Payee)
|
||||
assert.Equal(t, "", actualData.Transactions[0].Narration)
|
||||
|
||||
assert.Equal(t, "2024-01-02", actualData.transactions[1].date)
|
||||
assert.Equal(t, beancountDirectiveCompletedTransaction, actualData.transactions[1].directive)
|
||||
assert.Equal(t, "", actualData.transactions[1].payee)
|
||||
assert.Equal(t, "test\ttest2\ntest3", actualData.transactions[1].narration)
|
||||
assert.Equal(t, "2024-01-02", actualData.Transactions[1].Date)
|
||||
assert.Equal(t, beancountDirectiveCompletedTransaction, actualData.Transactions[1].Directive)
|
||||
assert.Equal(t, "", actualData.Transactions[1].Payee)
|
||||
assert.Equal(t, "test\ttest2\ntest3", actualData.Transactions[1].Narration)
|
||||
|
||||
assert.Equal(t, "2024-01-03", actualData.transactions[2].date)
|
||||
assert.Equal(t, beancountDirectiveInCompleteTransaction, actualData.transactions[2].directive)
|
||||
assert.Equal(t, "test", actualData.transactions[2].payee)
|
||||
assert.Equal(t, "test2", actualData.transactions[2].narration)
|
||||
assert.Equal(t, "2024-01-03", actualData.Transactions[2].Date)
|
||||
assert.Equal(t, beancountDirectiveInCompleteTransaction, actualData.Transactions[2].Directive)
|
||||
assert.Equal(t, "test", actualData.Transactions[2].Payee)
|
||||
assert.Equal(t, "test2", actualData.Transactions[2].Narration)
|
||||
|
||||
assert.Equal(t, "2024-01-04", actualData.transactions[3].date)
|
||||
assert.Equal(t, beancountDirectivePaddingTransaction, actualData.transactions[3].directive)
|
||||
assert.Equal(t, "", actualData.transactions[3].payee)
|
||||
assert.Equal(t, "test", actualData.transactions[3].narration)
|
||||
assert.Equal(t, "2024-01-04", actualData.Transactions[3].Date)
|
||||
assert.Equal(t, beancountDirectivePaddingTransaction, actualData.Transactions[3].Directive)
|
||||
assert.Equal(t, "", actualData.Transactions[3].Payee)
|
||||
assert.Equal(t, "test", actualData.Transactions[3].Narration)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.transactions[3].tags))
|
||||
assert.Equal(t, actualData.transactions[3].tags[0], "tag")
|
||||
assert.Equal(t, actualData.transactions[3].tags[1], "tag2")
|
||||
assert.Equal(t, 2, len(actualData.Transactions[3].Tags))
|
||||
assert.Equal(t, actualData.Transactions[3].Tags[0], "tag")
|
||||
assert.Equal(t, actualData.Transactions[3].Tags[1], "tag2")
|
||||
|
||||
assert.Equal(t, "2024-01-05", actualData.transactions[4].date)
|
||||
assert.Equal(t, beancountDirectiveTransaction, actualData.transactions[4].directive)
|
||||
assert.Equal(t, "", actualData.transactions[4].payee)
|
||||
assert.Equal(t, "test", actualData.transactions[4].narration)
|
||||
assert.Equal(t, "2024-01-05", actualData.Transactions[4].Date)
|
||||
assert.Equal(t, beancountDirectiveTransaction, actualData.Transactions[4].Directive)
|
||||
assert.Equal(t, "", actualData.Transactions[4].Payee)
|
||||
assert.Equal(t, "test", actualData.Transactions[4].Narration)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.transactions[4].links))
|
||||
assert.Equal(t, actualData.transactions[4].links[0], "scheme://path/to/test/link")
|
||||
assert.Equal(t, 1, len(actualData.Transactions[4].Links))
|
||||
assert.Equal(t, actualData.Transactions[4].Links[0], "scheme://path/to/test/link")
|
||||
|
||||
assert.Equal(t, "2024-01-06", actualData.transactions[5].date)
|
||||
assert.Equal(t, beancountDirectiveTransaction, actualData.transactions[5].directive)
|
||||
assert.Equal(t, "", actualData.transactions[5].payee)
|
||||
assert.Equal(t, "", actualData.transactions[5].narration)
|
||||
assert.Equal(t, "2024-01-06", actualData.Transactions[5].Date)
|
||||
assert.Equal(t, beancountDirectiveTransaction, actualData.Transactions[5].Directive)
|
||||
assert.Equal(t, "", actualData.Transactions[5].Payee)
|
||||
assert.Equal(t, "", actualData.Transactions[5].Narration)
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderReadTransactionPostingLine(t *testing.T) {
|
||||
@@ -331,39 +331,39 @@ func TestBeancountDataReaderReadTransactionPostingLine(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.transactions))
|
||||
assert.Equal(t, 2, len(actualData.Transactions))
|
||||
|
||||
assert.Equal(t, "2024-01-01", actualData.transactions[0].date)
|
||||
assert.Equal(t, 2, len(actualData.transactions[0].postings))
|
||||
assert.Equal(t, "Income:TestCategory", actualData.transactions[0].postings[0].account)
|
||||
assert.Equal(t, "-123.45", actualData.transactions[0].postings[0].amount)
|
||||
assert.Equal(t, "CNY", actualData.transactions[0].postings[0].commodity)
|
||||
assert.Equal(t, "2024-01-01", actualData.Transactions[0].Date)
|
||||
assert.Equal(t, 2, len(actualData.Transactions[0].Postings))
|
||||
assert.Equal(t, "Income:TestCategory", actualData.Transactions[0].Postings[0].Account)
|
||||
assert.Equal(t, "-123.45", actualData.Transactions[0].Postings[0].Amount)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[0].Postings[0].Commodity)
|
||||
|
||||
assert.Equal(t, "Assets:TestAccount", actualData.transactions[0].postings[1].account)
|
||||
assert.Equal(t, "123.45", actualData.transactions[0].postings[1].amount)
|
||||
assert.Equal(t, "CNY", actualData.transactions[0].postings[1].commodity)
|
||||
assert.Equal(t, "Assets:TestAccount", actualData.Transactions[0].Postings[1].Account)
|
||||
assert.Equal(t, "123.45", actualData.Transactions[0].Postings[1].Amount)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[0].Postings[1].Commodity)
|
||||
|
||||
assert.Equal(t, "2024-01-02", actualData.transactions[1].date)
|
||||
assert.Equal(t, 4, len(actualData.transactions[1].postings))
|
||||
assert.Equal(t, "2024-01-02", actualData.Transactions[1].Date)
|
||||
assert.Equal(t, 4, len(actualData.Transactions[1].Postings))
|
||||
|
||||
assert.Equal(t, "Liabilities:TestAccount2", actualData.transactions[1].postings[0].account)
|
||||
assert.Equal(t, "-0.23", actualData.transactions[1].postings[0].amount)
|
||||
assert.Equal(t, "USD", actualData.transactions[1].postings[0].commodity)
|
||||
assert.Equal(t, "Expenses:TestCategory2", actualData.transactions[1].postings[1].account)
|
||||
assert.Equal(t, "Liabilities:TestAccount2", actualData.Transactions[1].Postings[0].Account)
|
||||
assert.Equal(t, "-0.23", actualData.Transactions[1].Postings[0].Amount)
|
||||
assert.Equal(t, "USD", actualData.Transactions[1].Postings[0].Commodity)
|
||||
assert.Equal(t, "Expenses:TestCategory2", actualData.Transactions[1].Postings[1].Account)
|
||||
|
||||
assert.Equal(t, "0.12", actualData.transactions[1].postings[1].amount)
|
||||
assert.Equal(t, "USD", actualData.transactions[1].postings[1].commodity)
|
||||
assert.Equal(t, "0.84", actualData.transactions[1].postings[1].totalCost)
|
||||
assert.Equal(t, "CNY", actualData.transactions[1].postings[1].totalCostCommodity)
|
||||
assert.Equal(t, "Expenses:TestCategory3", actualData.transactions[1].postings[2].account)
|
||||
assert.Equal(t, "0.12", actualData.Transactions[1].Postings[1].Amount)
|
||||
assert.Equal(t, "USD", actualData.Transactions[1].Postings[1].Commodity)
|
||||
assert.Equal(t, "0.84", actualData.Transactions[1].Postings[1].TotalCost)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[1].Postings[1].TotalCostCommodity)
|
||||
assert.Equal(t, "Expenses:TestCategory3", actualData.Transactions[1].Postings[2].Account)
|
||||
|
||||
assert.Equal(t, "0.11", actualData.transactions[1].postings[2].amount)
|
||||
assert.Equal(t, "USD", actualData.transactions[1].postings[2].commodity)
|
||||
assert.Equal(t, "7.12", actualData.transactions[1].postings[2].price)
|
||||
assert.Equal(t, "CNY", actualData.transactions[1].postings[2].priceCommodity)
|
||||
assert.Equal(t, "0.11", actualData.Transactions[1].Postings[2].Amount)
|
||||
assert.Equal(t, "USD", actualData.Transactions[1].Postings[2].Commodity)
|
||||
assert.Equal(t, "7.12", actualData.Transactions[1].Postings[2].Price)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[1].Postings[2].PriceCommodity)
|
||||
|
||||
assert.Equal(t, "0.00", actualData.transactions[1].postings[3].amount)
|
||||
assert.Equal(t, "USD", actualData.transactions[1].postings[3].commodity)
|
||||
assert.Equal(t, "0.00", actualData.Transactions[1].Postings[3].Amount)
|
||||
assert.Equal(t, "USD", actualData.Transactions[1].Postings[3].Commodity)
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderReadTransactionPostingLine_AmountExpression(t *testing.T) {
|
||||
@@ -377,19 +377,19 @@ func TestBeancountDataReaderReadTransactionPostingLine_AmountExpression(t *testi
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.transactions))
|
||||
assert.Equal(t, 1, len(actualData.Transactions))
|
||||
|
||||
assert.Equal(t, "2024-01-01", actualData.transactions[0].date)
|
||||
assert.Equal(t, 2, len(actualData.transactions[0].postings))
|
||||
assert.Equal(t, "Income:TestCategory", actualData.transactions[0].postings[0].account)
|
||||
assert.Equal(t, "(1.2-3.4) * 5.6 / 7.8", actualData.transactions[0].postings[0].originalAmount)
|
||||
assert.Equal(t, "-1.58", actualData.transactions[0].postings[0].amount)
|
||||
assert.Equal(t, "CNY", actualData.transactions[0].postings[0].commodity)
|
||||
assert.Equal(t, "2024-01-01", actualData.Transactions[0].Date)
|
||||
assert.Equal(t, 2, len(actualData.Transactions[0].Postings))
|
||||
assert.Equal(t, "Income:TestCategory", actualData.Transactions[0].Postings[0].Account)
|
||||
assert.Equal(t, "(1.2-3.4) * 5.6 / 7.8", actualData.Transactions[0].Postings[0].OriginalAmount)
|
||||
assert.Equal(t, "-1.58", actualData.Transactions[0].Postings[0].Amount)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[0].Postings[0].Commodity)
|
||||
|
||||
assert.Equal(t, "Assets:TestAccount", actualData.transactions[0].postings[1].account)
|
||||
assert.Equal(t, "1.2 * 3.4/-5.6 - 7.8", actualData.transactions[0].postings[1].originalAmount)
|
||||
assert.Equal(t, "-8.53", actualData.transactions[0].postings[1].amount)
|
||||
assert.Equal(t, "CNY", actualData.transactions[0].postings[1].commodity)
|
||||
assert.Equal(t, "Assets:TestAccount", actualData.Transactions[0].Postings[1].Account)
|
||||
assert.Equal(t, "1.2 * 3.4/-5.6 - 7.8", actualData.Transactions[0].Postings[1].OriginalAmount)
|
||||
assert.Equal(t, "-8.53", actualData.Transactions[0].Postings[1].Amount)
|
||||
assert.Equal(t, "CNY", actualData.Transactions[0].Postings[1].Commodity)
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderReadTransactionPostingLine_InvalidAmountExpression(t *testing.T) {
|
||||
@@ -444,8 +444,8 @@ func TestBeancountDataReaderReadTransactionPostingLine_MissingAmount(t *testing.
|
||||
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(actualData.transactions))
|
||||
assert.Equal(t, 0, len(actualData.transactions[0].postings))
|
||||
assert.Equal(t, 1, len(actualData.Transactions))
|
||||
assert.Equal(t, 0, len(actualData.Transactions[0].Postings))
|
||||
|
||||
reader, err = createNewBeancountDataReader(context, []byte(""+
|
||||
"2024-01-01 *\n"+
|
||||
@@ -454,8 +454,8 @@ func TestBeancountDataReaderReadTransactionPostingLine_MissingAmount(t *testing.
|
||||
|
||||
actualData, err = reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(actualData.transactions))
|
||||
assert.Equal(t, 0, len(actualData.transactions[0].postings))
|
||||
assert.Equal(t, 1, len(actualData.Transactions))
|
||||
assert.Equal(t, 0, len(actualData.Transactions[0].Postings))
|
||||
}
|
||||
|
||||
func TestBeancountDataReaderReadTransactionPostingLine_MissingCommodity(t *testing.T) {
|
||||
@@ -503,18 +503,18 @@ func TestBeancountDataReaderReadTransactionMetadataLine(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.transactions))
|
||||
assert.Equal(t, 2, len(actualData.Transactions))
|
||||
|
||||
assert.Equal(t, "2024-01-01", actualData.transactions[0].date)
|
||||
assert.Equal(t, 2, len(actualData.transactions[0].postings))
|
||||
assert.Equal(t, 2, len(actualData.transactions[0].metadata))
|
||||
assert.Equal(t, "value", actualData.transactions[0].metadata["key"])
|
||||
assert.Equal(t, "value 2", actualData.transactions[0].metadata["key2"])
|
||||
assert.Equal(t, "2024-01-01", actualData.Transactions[0].Date)
|
||||
assert.Equal(t, 2, len(actualData.Transactions[0].Postings))
|
||||
assert.Equal(t, 2, len(actualData.Transactions[0].Metadata))
|
||||
assert.Equal(t, "value", actualData.Transactions[0].Metadata["key"])
|
||||
assert.Equal(t, "value 2", actualData.Transactions[0].Metadata["key2"])
|
||||
|
||||
assert.Equal(t, "2024-01-02", actualData.transactions[1].date)
|
||||
assert.Equal(t, 2, len(actualData.transactions[1].postings))
|
||||
assert.Equal(t, 2, len(actualData.transactions[1].postings[0].metadata))
|
||||
assert.Equal(t, "value6", actualData.transactions[1].postings[0].metadata["key6"])
|
||||
assert.Equal(t, "value 7", actualData.transactions[1].postings[0].metadata["key7"])
|
||||
assert.Equal(t, 0, len(actualData.transactions[1].postings[1].metadata))
|
||||
assert.Equal(t, "2024-01-02", actualData.Transactions[1].Date)
|
||||
assert.Equal(t, 2, len(actualData.Transactions[1].Postings))
|
||||
assert.Equal(t, 2, len(actualData.Transactions[1].Postings[0].Metadata))
|
||||
assert.Equal(t, "value6", actualData.Transactions[1].Postings[0].Metadata["key6"])
|
||||
assert.Equal(t, "value 7", actualData.Transactions[1].Postings[0].Metadata["key7"])
|
||||
assert.Equal(t, 0, len(actualData.Transactions[1].Postings[1].Metadata))
|
||||
}
|
||||
|
||||
@@ -8,34 +8,34 @@ import (
|
||||
|
||||
func TestBeancountAccount_IsOpeningBalanceEquityAccount_True(t *testing.T) {
|
||||
account := beancountAccount{
|
||||
accountType: beancountEquityAccountType,
|
||||
name: "Equity:Opening-Balances",
|
||||
AccountType: beancountEquityAccountType,
|
||||
Name: "Equity:Opening-Balances",
|
||||
}
|
||||
assert.True(t, account.isOpeningBalanceEquityAccount())
|
||||
|
||||
account = beancountAccount{
|
||||
accountType: beancountEquityAccountType,
|
||||
name: "E:Opening-Balances",
|
||||
AccountType: beancountEquityAccountType,
|
||||
Name: "E:Opening-Balances",
|
||||
}
|
||||
assert.True(t, account.isOpeningBalanceEquityAccount())
|
||||
}
|
||||
|
||||
func TestBeancountAccount_IsOpeningBalanceEquityAccount_False(t *testing.T) {
|
||||
account := beancountAccount{
|
||||
accountType: beancountAssetsAccountType,
|
||||
name: "Equity:Opening-Balances",
|
||||
AccountType: beancountAssetsAccountType,
|
||||
Name: "Equity:Opening-Balances",
|
||||
}
|
||||
assert.False(t, account.isOpeningBalanceEquityAccount())
|
||||
|
||||
account = beancountAccount{
|
||||
accountType: beancountEquityAccountType,
|
||||
name: "Opening-Balances",
|
||||
AccountType: beancountEquityAccountType,
|
||||
Name: "Opening-Balances",
|
||||
}
|
||||
assert.False(t, account.isOpeningBalanceEquityAccount())
|
||||
|
||||
account = beancountAccount{
|
||||
accountType: beancountEquityAccountType,
|
||||
name: "Equity:Other",
|
||||
AccountType: beancountEquityAccountType,
|
||||
Name: "Equity:Other",
|
||||
}
|
||||
assert.False(t, account.isOpeningBalanceEquityAccount())
|
||||
}
|
||||
|
||||
@@ -110,49 +110,49 @@ func (t *beancountTransactionDataRowIterator) Next(ctx core.Context, user *model
|
||||
func (t *beancountTransactionDataRowIterator) parseTransaction(ctx core.Context, user *models.User, beancountEntry *beancountTransactionEntry) (map[datatable.TransactionDataTableColumn]string, error) {
|
||||
data := make(map[datatable.TransactionDataTableColumn]string, len(beancountTransactionSupportedColumns))
|
||||
|
||||
if beancountEntry.date == "" {
|
||||
if beancountEntry.Date == "" {
|
||||
return nil, errs.ErrMissingTransactionTime
|
||||
}
|
||||
|
||||
// Beancount supports the international ISO 8601 standard format for dates, with dashes or the same ordering with slashes
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = strings.ReplaceAll(beancountEntry.date, "/", "-") + " 00:00:00"
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = strings.ReplaceAll(beancountEntry.Date, "/", "-") + " 00:00:00"
|
||||
|
||||
if len(beancountEntry.postings) == 2 {
|
||||
splitData1 := beancountEntry.postings[0]
|
||||
splitData2 := beancountEntry.postings[1]
|
||||
if len(beancountEntry.Postings) == 2 {
|
||||
splitData1 := beancountEntry.Postings[0]
|
||||
splitData2 := beancountEntry.Postings[1]
|
||||
|
||||
account1 := t.dataTable.accountMap[splitData1.account]
|
||||
account2 := t.dataTable.accountMap[splitData2.account]
|
||||
account1 := t.dataTable.accountMap[splitData1.Account]
|
||||
account2 := t.dataTable.accountMap[splitData2.Account]
|
||||
|
||||
if account1 == nil || account2 == nil {
|
||||
return nil, errs.ErrMissingAccountData
|
||||
}
|
||||
|
||||
amount1, err := utils.ParseAmount(splitData1.amount)
|
||||
amount1, err := utils.ParseAmount(splitData1.Amount)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse amount \"%s\", because %s", splitData1.amount, err.Error())
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse amount \"%s\", because %s", splitData1.Amount, err.Error())
|
||||
return nil, errs.ErrAmountInvalid
|
||||
}
|
||||
|
||||
amount2, err := utils.ParseAmount(splitData2.amount)
|
||||
amount2, err := utils.ParseAmount(splitData2.Amount)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse amount \"%s\", because %s", splitData2.amount, err.Error())
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse amount \"%s\", because %s", splitData2.Amount, err.Error())
|
||||
return nil, errs.ErrAmountInvalid
|
||||
}
|
||||
|
||||
if ((account1.accountType == beancountEquityAccountType || account1.accountType == beancountIncomeAccountType) && (account2.accountType == beancountAssetsAccountType || account2.accountType == beancountLiabilitiesAccountType)) ||
|
||||
((account2.accountType == beancountEquityAccountType || account2.accountType == beancountIncomeAccountType) && (account1.accountType == beancountAssetsAccountType || account1.accountType == beancountLiabilitiesAccountType)) { // income
|
||||
if ((account1.AccountType == beancountEquityAccountType || account1.AccountType == beancountIncomeAccountType) && (account2.AccountType == beancountAssetsAccountType || account2.AccountType == beancountLiabilitiesAccountType)) ||
|
||||
((account2.AccountType == beancountEquityAccountType || account2.AccountType == beancountIncomeAccountType) && (account1.AccountType == beancountAssetsAccountType || account1.AccountType == beancountLiabilitiesAccountType)) { // income
|
||||
fromAccount := account1
|
||||
toAccount := account2
|
||||
toCurrency := splitData2.commodity
|
||||
toCurrency := splitData2.Commodity
|
||||
toAmount := amount2
|
||||
|
||||
if (account2.accountType == beancountEquityAccountType || account2.accountType == beancountIncomeAccountType) && (account1.accountType == beancountAssetsAccountType || account1.accountType == beancountLiabilitiesAccountType) {
|
||||
if (account2.AccountType == beancountEquityAccountType || account2.AccountType == beancountIncomeAccountType) && (account1.AccountType == beancountAssetsAccountType || account1.AccountType == beancountLiabilitiesAccountType) {
|
||||
fromAccount = account2
|
||||
toAccount = account1
|
||||
toCurrency = splitData1.commodity
|
||||
toCurrency = splitData1.Commodity
|
||||
toAmount = amount1
|
||||
}
|
||||
|
||||
@@ -162,48 +162,48 @@ func (t *beancountTransactionDataRowIterator) parseTransaction(ctx core.Context,
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = utils.IntToString(int(models.TRANSACTION_TYPE_INCOME))
|
||||
}
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = fromAccount.name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = toAccount.name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = fromAccount.Name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = toAccount.Name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY] = toCurrency
|
||||
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(toAmount)
|
||||
} else if account1.accountType == beancountExpensesAccountType && (account2.accountType == beancountAssetsAccountType || account2.accountType == beancountLiabilitiesAccountType) ||
|
||||
(account2.accountType == beancountExpensesAccountType && (account1.accountType == beancountAssetsAccountType || account1.accountType == beancountLiabilitiesAccountType)) { // expense
|
||||
} else if account1.AccountType == beancountExpensesAccountType && (account2.AccountType == beancountAssetsAccountType || account2.AccountType == beancountLiabilitiesAccountType) ||
|
||||
(account2.AccountType == beancountExpensesAccountType && (account1.AccountType == beancountAssetsAccountType || account1.AccountType == beancountLiabilitiesAccountType)) { // expense
|
||||
fromAccount := account1
|
||||
fromCurrency := splitData1.commodity
|
||||
fromCurrency := splitData1.Commodity
|
||||
fromAmount := amount1
|
||||
toAccount := account2
|
||||
|
||||
if account1.accountType == beancountExpensesAccountType && (account2.accountType == beancountAssetsAccountType || account2.accountType == beancountLiabilitiesAccountType) {
|
||||
if account1.AccountType == beancountExpensesAccountType && (account2.AccountType == beancountAssetsAccountType || account2.AccountType == beancountLiabilitiesAccountType) {
|
||||
fromAccount = account2
|
||||
fromCurrency = splitData2.commodity
|
||||
fromCurrency = splitData2.Commodity
|
||||
fromAmount = amount2
|
||||
toAccount = account1
|
||||
}
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = utils.IntToString(int(models.TRANSACTION_TYPE_EXPENSE))
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = toAccount.name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = fromAccount.name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = toAccount.Name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = fromAccount.Name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY] = fromCurrency
|
||||
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(-fromAmount)
|
||||
} else if (account1.accountType == beancountAssetsAccountType || account1.accountType == beancountLiabilitiesAccountType) &&
|
||||
(account2.accountType == beancountAssetsAccountType || account2.accountType == beancountLiabilitiesAccountType) {
|
||||
} else if (account1.AccountType == beancountAssetsAccountType || account1.AccountType == beancountLiabilitiesAccountType) &&
|
||||
(account2.AccountType == beancountAssetsAccountType || account2.AccountType == beancountLiabilitiesAccountType) {
|
||||
var fromAccount, toAccount *beancountAccount
|
||||
var fromAmount, toAmount int64
|
||||
var fromCurrency, toCurrency string
|
||||
|
||||
if amount1 < 0 {
|
||||
fromAccount = account1
|
||||
fromCurrency = splitData1.commodity
|
||||
fromCurrency = splitData1.Commodity
|
||||
fromAmount = -amount1
|
||||
toAccount = account2
|
||||
toCurrency = splitData2.commodity
|
||||
toCurrency = splitData2.Commodity
|
||||
toAmount = amount2
|
||||
} else if amount2 < 0 {
|
||||
fromAccount = account2
|
||||
fromCurrency = splitData2.commodity
|
||||
fromCurrency = splitData2.Commodity
|
||||
fromAmount = -amount2
|
||||
toAccount = account1
|
||||
toCurrency = splitData1.commodity
|
||||
toCurrency = splitData1.Commodity
|
||||
toAmount = amount1
|
||||
} else {
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse transfer transaction, because unexcepted account amounts \"%d\" and \"%d\"", amount1, amount2)
|
||||
@@ -212,26 +212,26 @@ func (t *beancountTransactionDataRowIterator) parseTransaction(ctx core.Context,
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = utils.IntToString(int(models.TRANSACTION_TYPE_TRANSFER))
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = ""
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = fromAccount.name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = fromAccount.Name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY] = fromCurrency
|
||||
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(fromAmount)
|
||||
data[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME] = toAccount.name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME] = toAccount.Name
|
||||
data[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_CURRENCY] = toCurrency
|
||||
data[datatable.TRANSACTION_DATA_TABLE_RELATED_AMOUNT] = utils.FormatAmount(toAmount)
|
||||
} else {
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse transaction, because unexcepted account types \"%d\" and \"%d\"", account1.accountType, account2.accountType)
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse transaction, because unexcepted account types \"%d\" and \"%d\"", account1.AccountType, account2.AccountType)
|
||||
return nil, errs.ErrThereAreNotSupportedTransactionType
|
||||
}
|
||||
} else if len(beancountEntry.postings) <= 1 {
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse transaction, because postings count is %d", len(beancountEntry.postings))
|
||||
} else if len(beancountEntry.Postings) <= 1 {
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse transaction, because postings count is %d", len(beancountEntry.Postings))
|
||||
return nil, errs.ErrInvalidBeancountFile
|
||||
} else {
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse split transaction, because postings count is %d", len(beancountEntry.postings))
|
||||
log.Errorf(ctx, "[beancount_transaction_data_table.parseTransaction] cannot parse split transaction, because postings count is %d", len(beancountEntry.Postings))
|
||||
return nil, errs.ErrNotSupportedSplitTransactions
|
||||
}
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TAGS] = strings.Join(beancountEntry.tags, BEANCOUNT_TRANSACTION_TAG_SEPARATOR)
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = beancountEntry.narration
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TAGS] = strings.Join(beancountEntry.Tags, BEANCOUNT_TRANSACTION_TAG_SEPARATOR)
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = beancountEntry.Narration
|
||||
|
||||
return data, nil
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func createNewBeancountTransactionDataTable(beancountData *beancountData) (*bean
|
||||
}
|
||||
|
||||
return &beancountTransactionDataTable{
|
||||
allData: beancountData.transactions,
|
||||
accountMap: beancountData.accounts,
|
||||
allData: beancountData.Transactions,
|
||||
accountMap: beancountData.Accounts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -13,20 +13,20 @@ type iifAccountData struct {
|
||||
|
||||
// iifTransactionDataset defines the structure of intuit interchange format (iif) transaction dataset
|
||||
type iifTransactionDataset struct {
|
||||
transactionDataColumnIndexes map[string]int
|
||||
splitDataColumnIndexes map[string]int
|
||||
transactions []*iifTransactionData
|
||||
TransactionDataColumnIndexes map[string]int
|
||||
SplitDataColumnIndexes map[string]int
|
||||
Transactions []*iifTransactionData
|
||||
}
|
||||
|
||||
// iifTransactionData defines the structure of intuit interchange format (iif) transaction data
|
||||
type iifTransactionData struct {
|
||||
dataItems []string
|
||||
splitData []*iifTransactionSplitData
|
||||
DataItems []string
|
||||
SplitData []*iifTransactionSplitData
|
||||
}
|
||||
|
||||
// iifTransactionSplitData defines the structure of intuit interchange format (iif) transaction split data
|
||||
type iifTransactionSplitData struct {
|
||||
dataItems []string
|
||||
DataItems []string
|
||||
}
|
||||
|
||||
func (s *iifTransactionDataset) getTransactionDataItemValue(transactionData *iifTransactionData, columnName string) (string, bool) {
|
||||
@@ -34,13 +34,13 @@ func (s *iifTransactionDataset) getTransactionDataItemValue(transactionData *iif
|
||||
return "", false
|
||||
}
|
||||
|
||||
index, exists := s.transactionDataColumnIndexes[columnName]
|
||||
index, exists := s.TransactionDataColumnIndexes[columnName]
|
||||
|
||||
if !exists || index < 0 || index >= len(transactionData.dataItems) {
|
||||
if !exists || index < 0 || index >= len(transactionData.DataItems) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return transactionData.dataItems[index], true
|
||||
return transactionData.DataItems[index], true
|
||||
}
|
||||
|
||||
func (s *iifTransactionDataset) getSplitDataItemValue(splitData *iifTransactionSplitData, columnName string) (string, bool) {
|
||||
@@ -48,11 +48,11 @@ func (s *iifTransactionDataset) getSplitDataItemValue(splitData *iifTransactionS
|
||||
return "", false
|
||||
}
|
||||
|
||||
index, exists := s.splitDataColumnIndexes[columnName]
|
||||
index, exists := s.SplitDataColumnIndexes[columnName]
|
||||
|
||||
if !exists || index < 0 || index >= len(splitData.dataItems) {
|
||||
if !exists || index < 0 || index >= len(splitData.DataItems) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return splitData.dataItems[index], true
|
||||
return splitData.DataItems[index], true
|
||||
}
|
||||
|
||||
@@ -119,8 +119,8 @@ func (r *iifDataReader) read(ctx core.Context) ([]*iifAccountDataset, []*iifTran
|
||||
if lastLineSign == "" {
|
||||
if items[0] == iifTransactionLineSignColumnName {
|
||||
currentTransactionData = &iifTransactionData{
|
||||
dataItems: items,
|
||||
splitData: make([]*iifTransactionSplitData, 0),
|
||||
DataItems: items,
|
||||
SplitData: make([]*iifTransactionSplitData, 0),
|
||||
}
|
||||
lastLineSign = items[0]
|
||||
} else {
|
||||
@@ -134,8 +134,8 @@ func (r *iifDataReader) read(ctx core.Context) ([]*iifAccountDataset, []*iifTran
|
||||
return nil, nil, errs.ErrInvalidIIFFile
|
||||
}
|
||||
|
||||
currentTransactionData.splitData = append(currentTransactionData.splitData, &iifTransactionSplitData{
|
||||
dataItems: items,
|
||||
currentTransactionData.SplitData = append(currentTransactionData.SplitData, &iifTransactionSplitData{
|
||||
DataItems: items,
|
||||
})
|
||||
lastLineSign = items[0]
|
||||
} else if items[0] == iifTransactionEndLineSignColumnName {
|
||||
@@ -144,12 +144,12 @@ func (r *iifDataReader) read(ctx core.Context) ([]*iifAccountDataset, []*iifTran
|
||||
return nil, nil, errs.ErrInvalidIIFFile
|
||||
}
|
||||
|
||||
if len(currentTransactionData.splitData) < 1 {
|
||||
if len(currentTransactionData.SplitData) < 1 {
|
||||
log.Errorf(ctx, "[iif_data_reader.read] expected reading transaction split line, but read \"%s\"", items[0])
|
||||
return nil, nil, errs.ErrInvalidIIFFile
|
||||
}
|
||||
|
||||
currentTransactionDataset.transactions = append(currentTransactionDataset.transactions, currentTransactionData)
|
||||
currentTransactionDataset.Transactions = append(currentTransactionDataset.Transactions, currentTransactionData)
|
||||
lastLineSign = ""
|
||||
} else {
|
||||
log.Errorf(ctx, "[iif_data_reader.read] iif line expected reading split sign or transaction end sign, but actual is \"%s\"", items[0])
|
||||
@@ -234,9 +234,9 @@ func (r *iifDataReader) readTransactionSampleLines(ctx core.Context, items []str
|
||||
}
|
||||
|
||||
return &iifTransactionDataset{
|
||||
transactionDataColumnIndexes: transactionDataColumnIndexes,
|
||||
splitDataColumnIndexes: splitDataColumnIndexes,
|
||||
transactions: make([]*iifTransactionData, 0),
|
||||
TransactionDataColumnIndexes: transactionDataColumnIndexes,
|
||||
SplitDataColumnIndexes: splitDataColumnIndexes,
|
||||
Transactions: make([]*iifTransactionData, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -74,11 +74,11 @@ func (t *iifTransactionDataTable) TransactionRowCount() int {
|
||||
for i := 0; i < len(t.transactionDatasets); i++ {
|
||||
datasets := t.transactionDatasets[i]
|
||||
|
||||
for j := 0; j < len(datasets.transactions); j++ {
|
||||
transaction := datasets.transactions[j]
|
||||
for j := 0; j < len(datasets.Transactions); j++ {
|
||||
transaction := datasets.Transactions[j]
|
||||
|
||||
if transaction.splitData != nil {
|
||||
totalDataRowCount += len(transaction.splitData)
|
||||
if transaction.SplitData != nil {
|
||||
totalDataRowCount += len(transaction.SplitData)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,17 +122,17 @@ func (t *iifTransactionDataRowIterator) HasNext() bool {
|
||||
|
||||
currentDataset := allDatasets[t.currentDatasetIndex]
|
||||
|
||||
if t.currentIndexInDataset+1 < len(currentDataset.transactions) {
|
||||
if t.currentIndexInDataset+1 < len(currentDataset.Transactions) {
|
||||
return true
|
||||
} else if t.currentIndexInDataset < len(currentDataset.transactions) &&
|
||||
t.currentSplitDataIndex+1 < len(currentDataset.transactions[t.currentIndexInDataset].splitData) {
|
||||
} else if t.currentIndexInDataset < len(currentDataset.Transactions) &&
|
||||
t.currentSplitDataIndex+1 < len(currentDataset.Transactions[t.currentIndexInDataset].SplitData) {
|
||||
return true
|
||||
}
|
||||
|
||||
for i := t.currentDatasetIndex + 1; i < len(allDatasets); i++ {
|
||||
dataset := allDatasets[i]
|
||||
|
||||
if len(dataset.transactions) < 1 {
|
||||
if len(dataset.Transactions) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -150,8 +150,8 @@ func (t *iifTransactionDataRowIterator) Next(ctx core.Context, user *models.User
|
||||
foundNextRow := false
|
||||
dataset := allDatasets[i]
|
||||
|
||||
for j := t.currentIndexInDataset; j < len(dataset.transactions); j++ {
|
||||
if t.currentSplitDataIndex+1 < len(dataset.transactions[j].splitData) {
|
||||
for j := t.currentIndexInDataset; j < len(dataset.Transactions); j++ {
|
||||
if t.currentSplitDataIndex+1 < len(dataset.Transactions[j].SplitData) {
|
||||
t.currentSplitDataIndex++
|
||||
foundNextRow = true
|
||||
break
|
||||
@@ -176,22 +176,22 @@ func (t *iifTransactionDataRowIterator) Next(ctx core.Context, user *models.User
|
||||
|
||||
currentDataset := allDatasets[t.currentDatasetIndex]
|
||||
|
||||
if t.currentIndexInDataset >= len(currentDataset.transactions) {
|
||||
if t.currentIndexInDataset >= len(currentDataset.Transactions) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data := currentDataset.transactions[t.currentIndexInDataset]
|
||||
data := currentDataset.Transactions[t.currentIndexInDataset]
|
||||
|
||||
if len(data.splitData) < 1 {
|
||||
if len(data.SplitData) < 1 {
|
||||
log.Errorf(ctx, "[iif_transaction_data_table.Next] cannot parsing transaction in row#%d (dataset#%d), because split data is empty", t.currentIndexInDataset, t.currentDatasetIndex)
|
||||
return nil, errs.ErrInvalidIIFFile
|
||||
}
|
||||
|
||||
if t.currentSplitDataIndex >= len(data.splitData) {
|
||||
if t.currentSplitDataIndex >= len(data.SplitData) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(data.splitData) > 1 {
|
||||
if len(data.SplitData) > 1 {
|
||||
_, err := t.isSplitTransactionSupported(ctx, currentDataset, data)
|
||||
|
||||
if err != nil {
|
||||
@@ -222,11 +222,11 @@ func (t *iifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactionType, _ := dataset.getSplitDataItemValue(transactionData.splitData[splitDataIndex], iifTransactionTypeColumnName)
|
||||
transactionType, _ := dataset.getSplitDataItemValue(transactionData.SplitData[splitDataIndex], iifTransactionTypeColumnName)
|
||||
mainAccountName, _ := dataset.getTransactionDataItemValue(transactionData, iifTransactionAccountNameColumnName)
|
||||
splitAccountName, _ := dataset.getSplitDataItemValue(transactionData.splitData[splitDataIndex], iifTransactionAccountNameColumnName)
|
||||
splitAccountName, _ := dataset.getSplitDataItemValue(transactionData.SplitData[splitDataIndex], iifTransactionAccountNameColumnName)
|
||||
mainAmount, _ := dataset.getTransactionDataItemValue(transactionData, iifTransactionAmountColumnName)
|
||||
splitAmount, _ := dataset.getSplitDataItemValue(transactionData.splitData[splitDataIndex], iifTransactionAmountColumnName)
|
||||
splitAmount, _ := dataset.getSplitDataItemValue(transactionData.SplitData[splitDataIndex], iifTransactionAmountColumnName)
|
||||
mainAmountNum, err := parseAmount(mainAmount)
|
||||
|
||||
if err != nil {
|
||||
@@ -254,7 +254,7 @@ func (t *iifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
categoryName = mainAccountName
|
||||
accountName = splitAccountName
|
||||
|
||||
if len(transactionData.splitData) > 1 {
|
||||
if len(transactionData.SplitData) > 1 {
|
||||
amountNum = splitAmountNum
|
||||
} else {
|
||||
amountNum = -mainAmountNum
|
||||
@@ -263,7 +263,7 @@ func (t *iifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
categoryName = splitAccountName
|
||||
accountName = mainAccountName
|
||||
|
||||
if len(transactionData.splitData) > 1 {
|
||||
if len(transactionData.SplitData) > 1 {
|
||||
amountNum = -splitAmountNum
|
||||
} else {
|
||||
amountNum = mainAmountNum
|
||||
@@ -295,7 +295,7 @@ func (t *iifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
categoryName = mainAccountName
|
||||
accountName = splitAccountName
|
||||
|
||||
if len(transactionData.splitData) > 1 {
|
||||
if len(transactionData.SplitData) > 1 {
|
||||
amountNum = -splitAmountNum
|
||||
} else {
|
||||
amountNum = mainAmountNum
|
||||
@@ -304,7 +304,7 @@ func (t *iifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
categoryName = splitAccountName
|
||||
accountName = mainAccountName
|
||||
|
||||
if len(transactionData.splitData) > 1 {
|
||||
if len(transactionData.SplitData) > 1 {
|
||||
amountNum = splitAmountNum
|
||||
} else {
|
||||
amountNum = -mainAmountNum
|
||||
@@ -332,7 +332,7 @@ func (t *iifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
relatedAmountNum := int64(0)
|
||||
mainAccountTransferToSplitAccount := false
|
||||
|
||||
if len(transactionData.splitData) > 1 {
|
||||
if len(transactionData.SplitData) > 1 {
|
||||
amountNum = splitAmountNum
|
||||
relatedAmountNum = splitAmountNum
|
||||
mainAccountTransferToSplitAccount = amountNum >= 0
|
||||
@@ -369,11 +369,11 @@ func (t *iifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
}
|
||||
}
|
||||
|
||||
if splitMemo, _ := dataset.getSplitDataItemValue(transactionData.splitData[splitDataIndex], iifTransactionMemoColumnName); splitMemo != "" {
|
||||
if splitMemo, _ := dataset.getSplitDataItemValue(transactionData.SplitData[splitDataIndex], iifTransactionMemoColumnName); splitMemo != "" {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = splitMemo
|
||||
} else if memo, _ := dataset.getTransactionDataItemValue(transactionData, iifTransactionMemoColumnName); memo != "" {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = memo
|
||||
} else if splitName, _ := dataset.getSplitDataItemValue(transactionData.splitData[splitDataIndex], iifTransactionNameColumnName); splitName != "" {
|
||||
} else if splitName, _ := dataset.getSplitDataItemValue(transactionData.SplitData[splitDataIndex], iifTransactionNameColumnName); splitName != "" {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = splitName
|
||||
} else if name, _ := dataset.getTransactionDataItemValue(transactionData, iifTransactionNameColumnName); name != "" {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = name
|
||||
@@ -402,8 +402,8 @@ func (t *iifTransactionDataRowIterator) isSplitTransactionSupported(ctx core.Con
|
||||
|
||||
splitTotalAmount := int64(0)
|
||||
|
||||
for i := 0; i < len(transactionData.splitData); i++ {
|
||||
splitAmountStr, _ := dataset.getSplitDataItemValue(transactionData.splitData[i], iifTransactionAmountColumnName)
|
||||
for i := 0; i < len(transactionData.SplitData); i++ {
|
||||
splitAmountStr, _ := dataset.getSplitDataItemValue(transactionData.SplitData[i], iifTransactionAmountColumnName)
|
||||
splitAmount, err := parseAmount(splitAmountStr)
|
||||
|
||||
if err != nil {
|
||||
@@ -420,7 +420,7 @@ func (t *iifTransactionDataRowIterator) isSplitTransactionSupported(ctx core.Con
|
||||
}
|
||||
}
|
||||
|
||||
if len(transactionData.splitData) > 1 && !supportSplitTransactions {
|
||||
if len(transactionData.SplitData) > 1 && !supportSplitTransactions {
|
||||
return false, errs.ErrNotSupportedSplitTransactions
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ func createNewIIfTransactionDataTable(ctx core.Context, accountDatasets []*iifAc
|
||||
iifTransactionAccountNameColumnName,
|
||||
iifTransactionAmountColumnName,
|
||||
} {
|
||||
if _, exists := transactionDataset.transactionDataColumnIndexes[requiredColumnName]; !exists {
|
||||
if _, exists := transactionDataset.TransactionDataColumnIndexes[requiredColumnName]; !exists {
|
||||
return nil, errs.ErrMissingRequiredFieldInHeaderRow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,91 +34,91 @@ const (
|
||||
|
||||
// qifData defines the structure of quicken interchange format (qif) data
|
||||
type qifData struct {
|
||||
bankAccountTransactions []*qifTransactionData
|
||||
cashAccountTransactions []*qifTransactionData
|
||||
creditCardAccountTransactions []*qifTransactionData
|
||||
assetAccountTransactions []*qifTransactionData
|
||||
liabilityAccountTransactions []*qifTransactionData
|
||||
memorizedTransactions []*qifMemorizedTransactionData
|
||||
investmentAccountTransactions []*qifInvestmentTransactionData
|
||||
accounts []*qifAccountData
|
||||
categories []*qifCategoryData
|
||||
classes []*qifClassData
|
||||
BankAccountTransactions []*qifTransactionData
|
||||
CashAccountTransactions []*qifTransactionData
|
||||
CreditCardAccountTransactions []*qifTransactionData
|
||||
AssetAccountTransactions []*qifTransactionData
|
||||
LiabilityAccountTransactions []*qifTransactionData
|
||||
MemorizedTransactions []*qifMemorizedTransactionData
|
||||
InvestmentAccountTransactions []*qifInvestmentTransactionData
|
||||
Accounts []*qifAccountData
|
||||
Categories []*qifCategoryData
|
||||
Classes []*qifClassData
|
||||
}
|
||||
|
||||
// qifTransactionData defines the structure of quicken interchange format (qif) transaction data
|
||||
type qifTransactionData struct {
|
||||
date string
|
||||
amount string
|
||||
clearedStatus qifTransactionClearedStatus
|
||||
num string
|
||||
payee string
|
||||
memo string
|
||||
addresses []string
|
||||
category string
|
||||
subTransactionCategory []string
|
||||
subTransactionMemo []string
|
||||
subTransactionAmount []string
|
||||
account *qifAccountData
|
||||
Date string
|
||||
Amount string
|
||||
ClearedStatus qifTransactionClearedStatus
|
||||
Num string
|
||||
Payee string
|
||||
Memo string
|
||||
Addresses []string
|
||||
Category string
|
||||
SubTransactionCategory []string
|
||||
SubTransactionMemo []string
|
||||
SubTransactionAmount []string
|
||||
Account *qifAccountData
|
||||
}
|
||||
|
||||
// qifInvestmentTransactionData defines the structure of quicken interchange format (qif) investment transaction data
|
||||
type qifInvestmentTransactionData struct {
|
||||
date string
|
||||
action string
|
||||
security string
|
||||
price string
|
||||
quantity string
|
||||
amount string
|
||||
clearedStatus qifTransactionClearedStatus
|
||||
text string
|
||||
memo string
|
||||
commission string
|
||||
accountForTransfer string
|
||||
amountTransferred string
|
||||
account *qifAccountData
|
||||
Date string
|
||||
Action string
|
||||
Security string
|
||||
Price string
|
||||
Quantity string
|
||||
Amount string
|
||||
ClearedStatus qifTransactionClearedStatus
|
||||
Text string
|
||||
Memo string
|
||||
Commission string
|
||||
AccountForTransfer string
|
||||
AmountTransferred string
|
||||
Account *qifAccountData
|
||||
}
|
||||
|
||||
// qifMemorizedTransactionData defines the structure of quicken interchange format (qif) memorized transaction data
|
||||
type qifMemorizedTransactionData struct {
|
||||
qifTransactionData
|
||||
transactionType qifTransactionType
|
||||
amortization qifMemorizedTransactionAmortizationData
|
||||
TransactionType qifTransactionType
|
||||
Amortization qifMemorizedTransactionAmortizationData
|
||||
}
|
||||
|
||||
// qifMemorizedTransactionAmortizationData defines the structure of quicken interchange format (qif) memorized transaction amortization data
|
||||
type qifMemorizedTransactionAmortizationData struct {
|
||||
firstPaymentDate string
|
||||
totalYearsForLoan string
|
||||
numberOfPayments string
|
||||
numberOfPeriodsPerYear string
|
||||
interestRate string
|
||||
currentLoanBalance string
|
||||
originalLoanAmount string
|
||||
FirstPaymentDate string
|
||||
TotalYearsForLoan string
|
||||
NumberOfPayments string
|
||||
NumberOfPeriodsPerYear string
|
||||
InterestRate string
|
||||
CurrentLoanBalance string
|
||||
OriginalLoanAmount string
|
||||
}
|
||||
|
||||
// qifAccountData defines the structure of quicken interchange format (qif) account data
|
||||
type qifAccountData struct {
|
||||
name string
|
||||
accountType string
|
||||
description string
|
||||
creditLimit string
|
||||
statementBalanceDate string
|
||||
statementBalanceAmount string
|
||||
Name string
|
||||
AccountType string
|
||||
Description string
|
||||
CreditLimit string
|
||||
StatementBalanceDate string
|
||||
StatementBalanceAmount string
|
||||
}
|
||||
|
||||
// qifCategoryData defines the structure of quicken interchange format (qif) category data
|
||||
type qifCategoryData struct {
|
||||
name string
|
||||
description string
|
||||
taxRelated bool
|
||||
categoryType qifCategoryType
|
||||
budgetAmount string
|
||||
taxScheduleInformation string
|
||||
Name string
|
||||
Description string
|
||||
TaxRelated bool
|
||||
CategoryType qifCategoryType
|
||||
BudgetAmount string
|
||||
TaxScheduleInformation string
|
||||
}
|
||||
|
||||
// qifClassData defines the structure of quicken interchange format (qif) class data
|
||||
type qifClassData struct {
|
||||
name string
|
||||
description string
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
@@ -98,18 +98,18 @@ func (r *qifDataReader) read(ctx core.Context) (*qifData, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
transactionData.account = currentAccount
|
||||
transactionData.Account = currentAccount
|
||||
|
||||
if currentEntryHeader == qifBankTransactionHeader {
|
||||
data.bankAccountTransactions = append(data.bankAccountTransactions, transactionData)
|
||||
data.BankAccountTransactions = append(data.BankAccountTransactions, transactionData)
|
||||
} else if currentEntryHeader == qifCashTransactionHeader {
|
||||
data.cashAccountTransactions = append(data.cashAccountTransactions, transactionData)
|
||||
data.CashAccountTransactions = append(data.CashAccountTransactions, transactionData)
|
||||
} else if currentEntryHeader == qifCreditCardTransactionHeader {
|
||||
data.creditCardAccountTransactions = append(data.creditCardAccountTransactions, transactionData)
|
||||
data.CreditCardAccountTransactions = append(data.CreditCardAccountTransactions, transactionData)
|
||||
} else if currentEntryHeader == qifAssetAccountTransactionHeader {
|
||||
data.assetAccountTransactions = append(data.assetAccountTransactions, transactionData)
|
||||
data.AssetAccountTransactions = append(data.AssetAccountTransactions, transactionData)
|
||||
} else if currentEntryHeader == qifLiabilityAccountTransactionHeader {
|
||||
data.liabilityAccountTransactions = append(data.liabilityAccountTransactions, transactionData)
|
||||
data.LiabilityAccountTransactions = append(data.LiabilityAccountTransactions, transactionData)
|
||||
}
|
||||
} else if currentEntryHeader == qifMemorizedTransactionHeader || currentEntryHeader == qifMemorisedTransactionHeader {
|
||||
transactionData, err := r.parseMemorizedTransaction(ctx, entryData)
|
||||
@@ -122,8 +122,8 @@ func (r *qifDataReader) read(ctx core.Context) (*qifData, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
transactionData.account = currentAccount
|
||||
data.memorizedTransactions = append(data.memorizedTransactions, transactionData)
|
||||
transactionData.Account = currentAccount
|
||||
data.MemorizedTransactions = append(data.MemorizedTransactions, transactionData)
|
||||
} else if currentEntryHeader == qifInvestmentTransactionHeader {
|
||||
transactionData, err := r.parseInvestmentTransaction(ctx, entryData)
|
||||
|
||||
@@ -135,8 +135,8 @@ func (r *qifDataReader) read(ctx core.Context) (*qifData, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
transactionData.account = currentAccount
|
||||
data.investmentAccountTransactions = append(data.investmentAccountTransactions, transactionData)
|
||||
transactionData.Account = currentAccount
|
||||
data.InvestmentAccountTransactions = append(data.InvestmentAccountTransactions, transactionData)
|
||||
} else if currentEntryHeader == qifAccountHeader {
|
||||
accountData, err := r.parseAccount(ctx, entryData)
|
||||
|
||||
@@ -149,7 +149,7 @@ func (r *qifDataReader) read(ctx core.Context) (*qifData, error) {
|
||||
}
|
||||
|
||||
currentAccount = accountData
|
||||
data.accounts = append(data.accounts, accountData)
|
||||
data.Accounts = append(data.Accounts, accountData)
|
||||
} else if currentEntryHeader == qifCategoryHeader {
|
||||
categoryData, err := r.parseCategory(ctx, entryData)
|
||||
|
||||
@@ -161,7 +161,7 @@ func (r *qifDataReader) read(ctx core.Context) (*qifData, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
data.categories = append(data.categories, categoryData)
|
||||
data.Categories = append(data.Categories, categoryData)
|
||||
} else if currentEntryHeader == qifClassHeader {
|
||||
classData, err := r.parseClass(ctx, entryData)
|
||||
|
||||
@@ -173,7 +173,7 @@ func (r *qifDataReader) read(ctx core.Context) (*qifData, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
data.classes = append(data.classes, classData)
|
||||
data.Classes = append(data.Classes, classData)
|
||||
} else {
|
||||
log.Warnf(ctx, "[qif_data_reader.read] read unsupported entry header \"%s\" and skip this entry", currentEntryHeader)
|
||||
}
|
||||
@@ -202,27 +202,27 @@ func (r *qifDataReader) parseTransaction(ctx core.Context, data []string, ignore
|
||||
}
|
||||
|
||||
if line[0] == 'D' {
|
||||
transactionData.date = line[1:]
|
||||
transactionData.Date = line[1:]
|
||||
} else if line[0] == 'T' {
|
||||
transactionData.amount = line[1:]
|
||||
transactionData.Amount = line[1:]
|
||||
} else if line[0] == 'C' {
|
||||
transactionData.clearedStatus = r.parseClearedStatus(ctx, line[1:])
|
||||
transactionData.ClearedStatus = r.parseClearedStatus(ctx, line[1:])
|
||||
} else if line[0] == 'N' {
|
||||
transactionData.num = line[1:]
|
||||
transactionData.Num = line[1:]
|
||||
} else if line[0] == 'P' {
|
||||
transactionData.payee = line[1:]
|
||||
transactionData.Payee = line[1:]
|
||||
} else if line[0] == 'M' {
|
||||
transactionData.memo = line[1:]
|
||||
transactionData.Memo = line[1:]
|
||||
} else if line[0] == 'A' {
|
||||
transactionData.addresses = append(transactionData.addresses, line[1:])
|
||||
transactionData.Addresses = append(transactionData.Addresses, line[1:])
|
||||
} else if line[0] == 'L' {
|
||||
transactionData.category = line[1:]
|
||||
transactionData.Category = line[1:]
|
||||
} else if line[0] == 'S' {
|
||||
transactionData.subTransactionCategory = append(transactionData.subTransactionCategory, line[1:])
|
||||
transactionData.SubTransactionCategory = append(transactionData.SubTransactionCategory, line[1:])
|
||||
} else if line[0] == 'E' {
|
||||
transactionData.subTransactionMemo = append(transactionData.subTransactionMemo, line[1:])
|
||||
transactionData.SubTransactionMemo = append(transactionData.SubTransactionMemo, line[1:])
|
||||
} else if line[0] == '$' {
|
||||
transactionData.subTransactionAmount = append(transactionData.subTransactionAmount, line[1:])
|
||||
transactionData.SubTransactionAmount = append(transactionData.SubTransactionAmount, line[1:])
|
||||
} else {
|
||||
if !ignoreUnknown {
|
||||
log.Warnf(ctx, "[qif_data_reader.parseTransaction] read unsupported line \"%s\" and skip this line", line)
|
||||
@@ -247,7 +247,7 @@ func (r *qifDataReader) parseMemorizedTransaction(ctx core.Context, data []strin
|
||||
|
||||
transactionData := &qifMemorizedTransactionData{
|
||||
qifTransactionData: *baseTransactionData,
|
||||
amortization: qifMemorizedTransactionAmortizationData{},
|
||||
Amortization: qifMemorizedTransactionAmortizationData{},
|
||||
}
|
||||
|
||||
for i := 0; i < len(data); i++ {
|
||||
@@ -266,33 +266,33 @@ func (r *qifDataReader) parseMemorizedTransaction(ctx core.Context, data []strin
|
||||
|
||||
if line[0] == 'K' {
|
||||
if line == string(qifCheckTransactionType) {
|
||||
transactionData.transactionType = qifCheckTransactionType
|
||||
transactionData.TransactionType = qifCheckTransactionType
|
||||
} else if line == string(qifDepositTransactionType) {
|
||||
transactionData.transactionType = qifDepositTransactionType
|
||||
transactionData.TransactionType = qifDepositTransactionType
|
||||
} else if line == string(qifPaymentTransactionType) {
|
||||
transactionData.transactionType = qifPaymentTransactionType
|
||||
transactionData.TransactionType = qifPaymentTransactionType
|
||||
} else if line == string(qifInvestmentTransactionType) {
|
||||
transactionData.transactionType = qifInvestmentTransactionType
|
||||
transactionData.TransactionType = qifInvestmentTransactionType
|
||||
} else if line == string(qifElectronicPayeeTransactionType) {
|
||||
transactionData.transactionType = qifElectronicPayeeTransactionType
|
||||
transactionData.TransactionType = qifElectronicPayeeTransactionType
|
||||
} else {
|
||||
log.Warnf(ctx, "[qif_data_reader.parseMemorizedTransaction] read unsupported transaction type \"%s\" and skip this line", line)
|
||||
continue
|
||||
}
|
||||
} else if line[0] == '1' {
|
||||
transactionData.amortization.firstPaymentDate = line[1:]
|
||||
transactionData.Amortization.FirstPaymentDate = line[1:]
|
||||
} else if line[0] == '2' {
|
||||
transactionData.amortization.totalYearsForLoan = line[1:]
|
||||
transactionData.Amortization.TotalYearsForLoan = line[1:]
|
||||
} else if line[0] == '3' {
|
||||
transactionData.amortization.numberOfPayments = line[1:]
|
||||
transactionData.Amortization.NumberOfPayments = line[1:]
|
||||
} else if line[0] == '4' {
|
||||
transactionData.amortization.numberOfPeriodsPerYear = line[1:]
|
||||
transactionData.Amortization.NumberOfPeriodsPerYear = line[1:]
|
||||
} else if line[0] == '5' {
|
||||
transactionData.amortization.interestRate = line[1:]
|
||||
transactionData.Amortization.InterestRate = line[1:]
|
||||
} else if line[0] == '6' {
|
||||
transactionData.amortization.currentLoanBalance = line[1:]
|
||||
transactionData.Amortization.CurrentLoanBalance = line[1:]
|
||||
} else if line[0] == '7' {
|
||||
transactionData.amortization.originalLoanAmount = line[1:]
|
||||
transactionData.Amortization.OriginalLoanAmount = line[1:]
|
||||
} else {
|
||||
log.Warnf(ctx, "[qif_data_reader.parseMemorizedTransaction] read unsupported line \"%s\" and skip this line", line)
|
||||
continue
|
||||
@@ -317,29 +317,29 @@ func (r *qifDataReader) parseInvestmentTransaction(ctx core.Context, data []stri
|
||||
}
|
||||
|
||||
if line[0] == 'D' {
|
||||
transactionData.date = line[1:]
|
||||
transactionData.Date = line[1:]
|
||||
} else if line[0] == 'N' {
|
||||
transactionData.action = line[1:]
|
||||
transactionData.Action = line[1:]
|
||||
} else if line[0] == 'Y' {
|
||||
transactionData.security = line[1:]
|
||||
transactionData.Security = line[1:]
|
||||
} else if line[0] == 'I' {
|
||||
transactionData.price = line[1:]
|
||||
transactionData.Price = line[1:]
|
||||
} else if line[0] == 'Q' {
|
||||
transactionData.quantity = line[1:]
|
||||
transactionData.Quantity = line[1:]
|
||||
} else if line[0] == 'T' {
|
||||
transactionData.amount = line[1:]
|
||||
transactionData.Amount = line[1:]
|
||||
} else if line[0] == 'C' {
|
||||
transactionData.clearedStatus = r.parseClearedStatus(ctx, line[1:])
|
||||
transactionData.ClearedStatus = r.parseClearedStatus(ctx, line[1:])
|
||||
} else if line[0] == 'P' {
|
||||
transactionData.text = line[1:]
|
||||
transactionData.Text = line[1:]
|
||||
} else if line[0] == 'M' {
|
||||
transactionData.memo = line[1:]
|
||||
transactionData.Memo = line[1:]
|
||||
} else if line[0] == 'O' {
|
||||
transactionData.commission = line[1:]
|
||||
transactionData.Commission = line[1:]
|
||||
} else if line[0] == 'L' {
|
||||
transactionData.accountForTransfer = line[1:]
|
||||
transactionData.AccountForTransfer = line[1:]
|
||||
} else if line[0] == '$' {
|
||||
transactionData.amountTransferred = line[1:]
|
||||
transactionData.AmountTransferred = line[1:]
|
||||
} else {
|
||||
log.Warnf(ctx, "[qif_data_reader.parseInvestmentTransaction] read unsupported line \"%s\" and skip this line", line)
|
||||
continue
|
||||
@@ -364,17 +364,17 @@ func (r *qifDataReader) parseAccount(ctx core.Context, data []string) (*qifAccou
|
||||
}
|
||||
|
||||
if line[0] == 'N' {
|
||||
accountData.name = line[1:]
|
||||
accountData.Name = line[1:]
|
||||
} else if line[0] == 'T' {
|
||||
accountData.accountType = line[1:]
|
||||
accountData.AccountType = line[1:]
|
||||
} else if line[0] == 'D' {
|
||||
accountData.description = line[1:]
|
||||
accountData.Description = line[1:]
|
||||
} else if line[0] == 'L' {
|
||||
accountData.creditLimit = line[1:]
|
||||
accountData.CreditLimit = line[1:]
|
||||
} else if line[0] == '/' {
|
||||
accountData.statementBalanceDate = line[1:]
|
||||
accountData.StatementBalanceDate = line[1:]
|
||||
} else if line[0] == '$' {
|
||||
accountData.statementBalanceAmount = line[1:]
|
||||
accountData.StatementBalanceAmount = line[1:]
|
||||
} else {
|
||||
log.Warnf(ctx, "[qif_data_reader.parseAccount] read unsupported line \"%s\" and skip this line", line)
|
||||
continue
|
||||
@@ -399,27 +399,27 @@ func (r *qifDataReader) parseCategory(ctx core.Context, data []string) (*qifCate
|
||||
}
|
||||
|
||||
if line[0] == 'N' {
|
||||
categoryData.name = line[1:]
|
||||
categoryData.Name = line[1:]
|
||||
} else if line[0] == 'D' {
|
||||
categoryData.description = line[1:]
|
||||
categoryData.Description = line[1:]
|
||||
} else if line[0] == 'T' {
|
||||
categoryData.taxRelated = true
|
||||
categoryData.TaxRelated = true
|
||||
} else if line[0] == 'I' {
|
||||
categoryData.categoryType = qifIncomeTransaction
|
||||
categoryData.CategoryType = qifIncomeTransaction
|
||||
} else if line[0] == 'E' {
|
||||
categoryData.categoryType = qifExpenseTransaction
|
||||
categoryData.CategoryType = qifExpenseTransaction
|
||||
} else if line[0] == 'B' {
|
||||
categoryData.budgetAmount = line[1:]
|
||||
categoryData.BudgetAmount = line[1:]
|
||||
} else if line[0] == 'R' {
|
||||
categoryData.taxScheduleInformation = line[1:]
|
||||
categoryData.TaxScheduleInformation = line[1:]
|
||||
} else {
|
||||
log.Warnf(ctx, "[qif_data_reader.parseCategory] read unsupported line \"%s\" and skip this line", line)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if categoryData.categoryType == "" {
|
||||
categoryData.categoryType = qifExpenseTransaction
|
||||
if categoryData.CategoryType == "" {
|
||||
categoryData.CategoryType = qifExpenseTransaction
|
||||
}
|
||||
|
||||
return categoryData, nil
|
||||
@@ -440,9 +440,9 @@ func (r *qifDataReader) parseClass(ctx core.Context, data []string) (*qifClassDa
|
||||
}
|
||||
|
||||
if line[0] == 'N' {
|
||||
classData.name = line[1:]
|
||||
classData.Name = line[1:]
|
||||
} else if line[0] == 'D' {
|
||||
classData.description = line[1:]
|
||||
classData.Description = line[1:]
|
||||
} else {
|
||||
log.Warnf(ctx, "[qif_data_reader.parseClass] read unsupported line \"%s\" and skip this line", line)
|
||||
continue
|
||||
|
||||
@@ -64,48 +64,48 @@ func TestQifDataReaderParse(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.bankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.bankAccountTransactions[0].date)
|
||||
assert.Equal(t, "-123.45", actualData.bankAccountTransactions[0].amount)
|
||||
assert.Equal(t, "2024/10/12", actualData.bankAccountTransactions[1].date)
|
||||
assert.Equal(t, "+234.56", actualData.bankAccountTransactions[1].amount)
|
||||
assert.Equal(t, 2, len(actualData.BankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.BankAccountTransactions[0].Date)
|
||||
assert.Equal(t, "-123.45", actualData.BankAccountTransactions[0].Amount)
|
||||
assert.Equal(t, "2024/10/12", actualData.BankAccountTransactions[1].Date)
|
||||
assert.Equal(t, "+234.56", actualData.BankAccountTransactions[1].Amount)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.cashAccountTransactions))
|
||||
assert.Equal(t, "2024/9/1", actualData.cashAccountTransactions[0].date)
|
||||
assert.Equal(t, "100.00", actualData.cashAccountTransactions[0].amount)
|
||||
assert.Equal(t, "Opening Balance", actualData.cashAccountTransactions[0].payee)
|
||||
assert.Equal(t, "[Wallet]", actualData.cashAccountTransactions[0].category)
|
||||
assert.Equal(t, 1, len(actualData.CashAccountTransactions))
|
||||
assert.Equal(t, "2024/9/1", actualData.CashAccountTransactions[0].Date)
|
||||
assert.Equal(t, "100.00", actualData.CashAccountTransactions[0].Amount)
|
||||
assert.Equal(t, "Opening Balance", actualData.CashAccountTransactions[0].Payee)
|
||||
assert.Equal(t, "[Wallet]", actualData.CashAccountTransactions[0].Category)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.memorizedTransactions))
|
||||
assert.Equal(t, qifCheckTransactionType, actualData.memorizedTransactions[0].transactionType)
|
||||
assert.Equal(t, "-123.45", actualData.memorizedTransactions[0].amount)
|
||||
assert.Equal(t, "2024/10/13", actualData.memorizedTransactions[0].amortization.firstPaymentDate)
|
||||
assert.Equal(t, "3", actualData.memorizedTransactions[0].amortization.totalYearsForLoan)
|
||||
assert.Equal(t, "1", actualData.memorizedTransactions[0].amortization.numberOfPayments)
|
||||
assert.Equal(t, "2", actualData.memorizedTransactions[0].amortization.numberOfPeriodsPerYear)
|
||||
assert.Equal(t, "12.34", actualData.memorizedTransactions[0].amortization.interestRate)
|
||||
assert.Equal(t, "100.45", actualData.memorizedTransactions[0].amortization.currentLoanBalance)
|
||||
assert.Equal(t, "234.56", actualData.memorizedTransactions[0].amortization.originalLoanAmount)
|
||||
assert.Equal(t, 1, len(actualData.MemorizedTransactions))
|
||||
assert.Equal(t, qifCheckTransactionType, actualData.MemorizedTransactions[0].TransactionType)
|
||||
assert.Equal(t, "-123.45", actualData.MemorizedTransactions[0].Amount)
|
||||
assert.Equal(t, "2024/10/13", actualData.MemorizedTransactions[0].Amortization.FirstPaymentDate)
|
||||
assert.Equal(t, "3", actualData.MemorizedTransactions[0].Amortization.TotalYearsForLoan)
|
||||
assert.Equal(t, "1", actualData.MemorizedTransactions[0].Amortization.NumberOfPayments)
|
||||
assert.Equal(t, "2", actualData.MemorizedTransactions[0].Amortization.NumberOfPeriodsPerYear)
|
||||
assert.Equal(t, "12.34", actualData.MemorizedTransactions[0].Amortization.InterestRate)
|
||||
assert.Equal(t, "100.45", actualData.MemorizedTransactions[0].Amortization.CurrentLoanBalance)
|
||||
assert.Equal(t, "234.56", actualData.MemorizedTransactions[0].Amortization.OriginalLoanAmount)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.investmentAccountTransactions))
|
||||
assert.Equal(t, "2024/10/14", actualData.investmentAccountTransactions[0].date)
|
||||
assert.Equal(t, "Buy", actualData.investmentAccountTransactions[0].action)
|
||||
assert.Equal(t, "Test", actualData.investmentAccountTransactions[0].security)
|
||||
assert.Equal(t, "12.34", actualData.investmentAccountTransactions[0].price)
|
||||
assert.Equal(t, "10", actualData.investmentAccountTransactions[0].quantity)
|
||||
assert.Equal(t, "-123.4", actualData.investmentAccountTransactions[0].amount)
|
||||
assert.Equal(t, 1, len(actualData.InvestmentAccountTransactions))
|
||||
assert.Equal(t, "2024/10/14", actualData.InvestmentAccountTransactions[0].Date)
|
||||
assert.Equal(t, "Buy", actualData.InvestmentAccountTransactions[0].Action)
|
||||
assert.Equal(t, "Test", actualData.InvestmentAccountTransactions[0].Security)
|
||||
assert.Equal(t, "12.34", actualData.InvestmentAccountTransactions[0].Price)
|
||||
assert.Equal(t, "10", actualData.InvestmentAccountTransactions[0].Quantity)
|
||||
assert.Equal(t, "-123.4", actualData.InvestmentAccountTransactions[0].Amount)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.accounts))
|
||||
assert.Equal(t, "Test Account", actualData.accounts[0].name)
|
||||
assert.Equal(t, "Wallet", actualData.accounts[1].name)
|
||||
assert.Equal(t, 2, len(actualData.Accounts))
|
||||
assert.Equal(t, "Test Account", actualData.Accounts[0].Name)
|
||||
assert.Equal(t, "Wallet", actualData.Accounts[1].Name)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.categories))
|
||||
assert.Equal(t, "Test Category", actualData.categories[0].name)
|
||||
assert.Equal(t, qifIncomeTransaction, actualData.categories[0].categoryType)
|
||||
assert.Equal(t, 1, len(actualData.Categories))
|
||||
assert.Equal(t, "Test Category", actualData.Categories[0].Name)
|
||||
assert.Equal(t, qifIncomeTransaction, actualData.Categories[0].CategoryType)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.classes))
|
||||
assert.Equal(t, "Test Class", actualData.classes[0].name)
|
||||
assert.Equal(t, "Foo Bar", actualData.classes[0].description)
|
||||
assert.Equal(t, 1, len(actualData.Classes))
|
||||
assert.Equal(t, "Test Class", actualData.Classes[0].Name)
|
||||
assert.Equal(t, "Foo Bar", actualData.Classes[0].Description)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParse_AccountEntryBeforeTransaction(t *testing.T) {
|
||||
@@ -137,21 +137,21 @@ func TestQifDataReaderParse_AccountEntryBeforeTransaction(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.bankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.bankAccountTransactions[0].date)
|
||||
assert.Equal(t, "-123.45", actualData.bankAccountTransactions[0].amount)
|
||||
assert.Equal(t, "2024/10/12", actualData.bankAccountTransactions[1].date)
|
||||
assert.Equal(t, "+234.56", actualData.bankAccountTransactions[1].amount)
|
||||
assert.Equal(t, 2, len(actualData.BankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.BankAccountTransactions[0].Date)
|
||||
assert.Equal(t, "-123.45", actualData.BankAccountTransactions[0].Amount)
|
||||
assert.Equal(t, "2024/10/12", actualData.BankAccountTransactions[1].Date)
|
||||
assert.Equal(t, "+234.56", actualData.BankAccountTransactions[1].Amount)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.cashAccountTransactions))
|
||||
assert.Equal(t, "2024/9/1", actualData.cashAccountTransactions[0].date)
|
||||
assert.Equal(t, "100.00", actualData.cashAccountTransactions[0].amount)
|
||||
assert.Equal(t, "Opening Balance", actualData.cashAccountTransactions[0].payee)
|
||||
assert.Equal(t, "[Wallet]", actualData.cashAccountTransactions[0].category)
|
||||
assert.Equal(t, 1, len(actualData.CashAccountTransactions))
|
||||
assert.Equal(t, "2024/9/1", actualData.CashAccountTransactions[0].Date)
|
||||
assert.Equal(t, "100.00", actualData.CashAccountTransactions[0].Amount)
|
||||
assert.Equal(t, "Opening Balance", actualData.CashAccountTransactions[0].Payee)
|
||||
assert.Equal(t, "[Wallet]", actualData.CashAccountTransactions[0].Category)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.accounts))
|
||||
assert.Equal(t, "Test Account", actualData.accounts[0].name)
|
||||
assert.Equal(t, "Wallet", actualData.accounts[1].name)
|
||||
assert.Equal(t, 2, len(actualData.Accounts))
|
||||
assert.Equal(t, "Test Account", actualData.Accounts[0].Name)
|
||||
assert.Equal(t, "Wallet", actualData.Accounts[1].Name)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParse_EmptyContent(t *testing.T) {
|
||||
@@ -188,13 +188,13 @@ func TestQifDataReaderParse_EmptyEntry(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 0, len(actualData.bankAccountTransactions))
|
||||
assert.Equal(t, 0, len(actualData.cashAccountTransactions))
|
||||
assert.Equal(t, 0, len(actualData.memorizedTransactions))
|
||||
assert.Equal(t, 0, len(actualData.investmentAccountTransactions))
|
||||
assert.Equal(t, 0, len(actualData.accounts))
|
||||
assert.Equal(t, 0, len(actualData.categories))
|
||||
assert.Equal(t, 0, len(actualData.classes))
|
||||
assert.Equal(t, 0, len(actualData.BankAccountTransactions))
|
||||
assert.Equal(t, 0, len(actualData.CashAccountTransactions))
|
||||
assert.Equal(t, 0, len(actualData.MemorizedTransactions))
|
||||
assert.Equal(t, 0, len(actualData.InvestmentAccountTransactions))
|
||||
assert.Equal(t, 0, len(actualData.Accounts))
|
||||
assert.Equal(t, 0, len(actualData.Categories))
|
||||
assert.Equal(t, 0, len(actualData.Classes))
|
||||
}
|
||||
|
||||
func TestQifDataReaderParse_UnsupportedEntryHeader(t *testing.T) {
|
||||
@@ -215,9 +215,9 @@ func TestQifDataReaderParse_UnsupportedEntryHeader(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(actualData.bankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.bankAccountTransactions[0].date)
|
||||
assert.Equal(t, "-123.45", actualData.bankAccountTransactions[0].amount)
|
||||
assert.Equal(t, 1, len(actualData.BankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.BankAccountTransactions[0].Date)
|
||||
assert.Equal(t, "-123.45", actualData.BankAccountTransactions[0].Amount)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParse_UnsupportedLine(t *testing.T) {
|
||||
@@ -238,11 +238,11 @@ func TestQifDataReaderParse_UnsupportedLine(t *testing.T) {
|
||||
actualData, err := reader.read(context)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(actualData.bankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.bankAccountTransactions[0].date)
|
||||
assert.Equal(t, "-123.45", actualData.bankAccountTransactions[0].amount)
|
||||
assert.Equal(t, "2024/10/11", actualData.bankAccountTransactions[1].date)
|
||||
assert.Equal(t, "100.00", actualData.bankAccountTransactions[1].amount)
|
||||
assert.Equal(t, 2, len(actualData.BankAccountTransactions))
|
||||
assert.Equal(t, "2024/10/9", actualData.BankAccountTransactions[0].Date)
|
||||
assert.Equal(t, "-123.45", actualData.BankAccountTransactions[0].Amount)
|
||||
assert.Equal(t, "2024/10/11", actualData.BankAccountTransactions[1].Date)
|
||||
assert.Equal(t, "100.00", actualData.BankAccountTransactions[1].Amount)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParse_NewEntryHeaderAfterUnclosedEntry(t *testing.T) {
|
||||
@@ -289,26 +289,26 @@ func TestQifDataReaderParseTransaction_SupportedFields(t *testing.T) {
|
||||
}, false)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "2024/10/12", actualData.date)
|
||||
assert.Equal(t, "-123.45", actualData.amount)
|
||||
assert.Equal(t, qifClearedStatusUnreconciled, actualData.clearedStatus)
|
||||
assert.Equal(t, "100", actualData.num)
|
||||
assert.Equal(t, "Foo", actualData.payee)
|
||||
assert.Equal(t, "Bar", actualData.memo)
|
||||
assert.Equal(t, 3, len(actualData.addresses))
|
||||
assert.Equal(t, "Address 1", actualData.addresses[0])
|
||||
assert.Equal(t, "Address 2", actualData.addresses[1])
|
||||
assert.Equal(t, "Address 3", actualData.addresses[2])
|
||||
assert.Equal(t, "Test Category", actualData.category)
|
||||
assert.Equal(t, 2, len(actualData.subTransactionCategory))
|
||||
assert.Equal(t, "Part1 Category", actualData.subTransactionCategory[0])
|
||||
assert.Equal(t, "Part2 Category", actualData.subTransactionCategory[1])
|
||||
assert.Equal(t, 2, len(actualData.subTransactionMemo))
|
||||
assert.Equal(t, "Part1 Memo", actualData.subTransactionMemo[0])
|
||||
assert.Equal(t, "Part2 Memo", actualData.subTransactionMemo[1])
|
||||
assert.Equal(t, 2, len(actualData.subTransactionAmount))
|
||||
assert.Equal(t, "-100.00", actualData.subTransactionAmount[0])
|
||||
assert.Equal(t, "-23.45", actualData.subTransactionAmount[1])
|
||||
assert.Equal(t, "2024/10/12", actualData.Date)
|
||||
assert.Equal(t, "-123.45", actualData.Amount)
|
||||
assert.Equal(t, qifClearedStatusUnreconciled, actualData.ClearedStatus)
|
||||
assert.Equal(t, "100", actualData.Num)
|
||||
assert.Equal(t, "Foo", actualData.Payee)
|
||||
assert.Equal(t, "Bar", actualData.Memo)
|
||||
assert.Equal(t, 3, len(actualData.Addresses))
|
||||
assert.Equal(t, "Address 1", actualData.Addresses[0])
|
||||
assert.Equal(t, "Address 2", actualData.Addresses[1])
|
||||
assert.Equal(t, "Address 3", actualData.Addresses[2])
|
||||
assert.Equal(t, "Test Category", actualData.Category)
|
||||
assert.Equal(t, 2, len(actualData.SubTransactionCategory))
|
||||
assert.Equal(t, "Part1 Category", actualData.SubTransactionCategory[0])
|
||||
assert.Equal(t, "Part2 Category", actualData.SubTransactionCategory[1])
|
||||
assert.Equal(t, 2, len(actualData.SubTransactionMemo))
|
||||
assert.Equal(t, "Part1 Memo", actualData.SubTransactionMemo[0])
|
||||
assert.Equal(t, "Part2 Memo", actualData.SubTransactionMemo[1])
|
||||
assert.Equal(t, 2, len(actualData.SubTransactionAmount))
|
||||
assert.Equal(t, "-100.00", actualData.SubTransactionAmount[0])
|
||||
assert.Equal(t, "-23.45", actualData.SubTransactionAmount[1])
|
||||
}
|
||||
|
||||
func TestQifDataReaderParseMemorizedTransaction_SupportedFields(t *testing.T) {
|
||||
@@ -333,36 +333,36 @@ func TestQifDataReaderParseMemorizedTransaction_SupportedFields(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, qifCheckTransactionType, actualData.transactionType)
|
||||
assert.Equal(t, "2024/10/12", actualData.date)
|
||||
assert.Equal(t, "-123.45", actualData.amount)
|
||||
assert.Equal(t, qifClearedStatusCleared, actualData.clearedStatus)
|
||||
assert.Equal(t, "100", actualData.num)
|
||||
assert.Equal(t, "Foo", actualData.payee)
|
||||
assert.Equal(t, "Bar", actualData.memo)
|
||||
assert.Equal(t, "2024/10/13", actualData.amortization.firstPaymentDate)
|
||||
assert.Equal(t, "3", actualData.amortization.totalYearsForLoan)
|
||||
assert.Equal(t, "1", actualData.amortization.numberOfPayments)
|
||||
assert.Equal(t, "2", actualData.amortization.numberOfPeriodsPerYear)
|
||||
assert.Equal(t, "12.34", actualData.amortization.interestRate)
|
||||
assert.Equal(t, "100.45", actualData.amortization.currentLoanBalance)
|
||||
assert.Equal(t, "234.56", actualData.amortization.originalLoanAmount)
|
||||
assert.Equal(t, qifCheckTransactionType, actualData.TransactionType)
|
||||
assert.Equal(t, "2024/10/12", actualData.Date)
|
||||
assert.Equal(t, "-123.45", actualData.Amount)
|
||||
assert.Equal(t, qifClearedStatusCleared, actualData.ClearedStatus)
|
||||
assert.Equal(t, "100", actualData.Num)
|
||||
assert.Equal(t, "Foo", actualData.Payee)
|
||||
assert.Equal(t, "Bar", actualData.Memo)
|
||||
assert.Equal(t, "2024/10/13", actualData.Amortization.FirstPaymentDate)
|
||||
assert.Equal(t, "3", actualData.Amortization.TotalYearsForLoan)
|
||||
assert.Equal(t, "1", actualData.Amortization.NumberOfPayments)
|
||||
assert.Equal(t, "2", actualData.Amortization.NumberOfPeriodsPerYear)
|
||||
assert.Equal(t, "12.34", actualData.Amortization.InterestRate)
|
||||
assert.Equal(t, "100.45", actualData.Amortization.CurrentLoanBalance)
|
||||
assert.Equal(t, "234.56", actualData.Amortization.OriginalLoanAmount)
|
||||
|
||||
actualData, err = reader.parseMemorizedTransaction(context, []string{"KD"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, qifDepositTransactionType, actualData.transactionType)
|
||||
assert.Equal(t, qifDepositTransactionType, actualData.TransactionType)
|
||||
|
||||
actualData, err = reader.parseMemorizedTransaction(context, []string{"KP"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, qifPaymentTransactionType, actualData.transactionType)
|
||||
assert.Equal(t, qifPaymentTransactionType, actualData.TransactionType)
|
||||
|
||||
actualData, err = reader.parseMemorizedTransaction(context, []string{"KI"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, qifInvestmentTransactionType, actualData.transactionType)
|
||||
assert.Equal(t, qifInvestmentTransactionType, actualData.TransactionType)
|
||||
|
||||
actualData, err = reader.parseMemorizedTransaction(context, []string{"KE"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, qifElectronicPayeeTransactionType, actualData.transactionType)
|
||||
assert.Equal(t, qifElectronicPayeeTransactionType, actualData.TransactionType)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParseInvestmentTransaction_SupportedFields(t *testing.T) {
|
||||
@@ -385,18 +385,18 @@ func TestQifDataReaderParseInvestmentTransaction_SupportedFields(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "2024/10/12", actualData.date)
|
||||
assert.Equal(t, "Buy", actualData.action)
|
||||
assert.Equal(t, "Test", actualData.security)
|
||||
assert.Equal(t, "12.34", actualData.price)
|
||||
assert.Equal(t, "10", actualData.quantity)
|
||||
assert.Equal(t, "-123.4", actualData.amount)
|
||||
assert.Equal(t, qifClearedStatusReconciled, actualData.clearedStatus)
|
||||
assert.Equal(t, "Foo", actualData.text)
|
||||
assert.Equal(t, "Bar", actualData.memo)
|
||||
assert.Equal(t, "Test2", actualData.commission)
|
||||
assert.Equal(t, "Account Name", actualData.accountForTransfer)
|
||||
assert.Equal(t, "100", actualData.amountTransferred)
|
||||
assert.Equal(t, "2024/10/12", actualData.Date)
|
||||
assert.Equal(t, "Buy", actualData.Action)
|
||||
assert.Equal(t, "Test", actualData.Security)
|
||||
assert.Equal(t, "12.34", actualData.Price)
|
||||
assert.Equal(t, "10", actualData.Quantity)
|
||||
assert.Equal(t, "-123.4", actualData.Amount)
|
||||
assert.Equal(t, qifClearedStatusReconciled, actualData.ClearedStatus)
|
||||
assert.Equal(t, "Foo", actualData.Text)
|
||||
assert.Equal(t, "Bar", actualData.Memo)
|
||||
assert.Equal(t, "Test2", actualData.Commission)
|
||||
assert.Equal(t, "Account Name", actualData.AccountForTransfer)
|
||||
assert.Equal(t, "100", actualData.AmountTransferred)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParseAccount_SupportedFields(t *testing.T) {
|
||||
@@ -413,12 +413,12 @@ func TestQifDataReaderParseAccount_SupportedFields(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Account Name", actualData.name)
|
||||
assert.Equal(t, "Account Type", actualData.accountType)
|
||||
assert.Equal(t, "Some Text", actualData.description)
|
||||
assert.Equal(t, "1234.56", actualData.creditLimit)
|
||||
assert.Equal(t, "2024/10/12", actualData.statementBalanceDate)
|
||||
assert.Equal(t, "123.45", actualData.statementBalanceAmount)
|
||||
assert.Equal(t, "Account Name", actualData.Name)
|
||||
assert.Equal(t, "Account Type", actualData.AccountType)
|
||||
assert.Equal(t, "Some Text", actualData.Description)
|
||||
assert.Equal(t, "1234.56", actualData.CreditLimit)
|
||||
assert.Equal(t, "2024/10/12", actualData.StatementBalanceDate)
|
||||
assert.Equal(t, "123.45", actualData.StatementBalanceAmount)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParseCategory_SupportedFields(t *testing.T) {
|
||||
@@ -435,12 +435,12 @@ func TestQifDataReaderParseCategory_SupportedFields(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Category Name:Sub Category Name", actualData.name)
|
||||
assert.Equal(t, "Some Text", actualData.description)
|
||||
assert.Equal(t, true, actualData.taxRelated)
|
||||
assert.Equal(t, qifIncomeTransaction, actualData.categoryType)
|
||||
assert.Equal(t, "123.45", actualData.budgetAmount)
|
||||
assert.Equal(t, "Test", actualData.taxScheduleInformation)
|
||||
assert.Equal(t, "Category Name:Sub Category Name", actualData.Name)
|
||||
assert.Equal(t, "Some Text", actualData.Description)
|
||||
assert.Equal(t, true, actualData.TaxRelated)
|
||||
assert.Equal(t, qifIncomeTransaction, actualData.CategoryType)
|
||||
assert.Equal(t, "123.45", actualData.BudgetAmount)
|
||||
assert.Equal(t, "Test", actualData.TaxScheduleInformation)
|
||||
|
||||
actualData2, err := reader.parseCategory(context, []string{
|
||||
"NCategory Name:Sub Category Name",
|
||||
@@ -449,10 +449,10 @@ func TestQifDataReaderParseCategory_SupportedFields(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Category Name:Sub Category Name", actualData2.name)
|
||||
assert.Equal(t, "Some Text", actualData2.description)
|
||||
assert.Equal(t, false, actualData2.taxRelated)
|
||||
assert.Equal(t, qifExpenseTransaction, actualData2.categoryType)
|
||||
assert.Equal(t, "Category Name:Sub Category Name", actualData2.Name)
|
||||
assert.Equal(t, "Some Text", actualData2.Description)
|
||||
assert.Equal(t, false, actualData2.TaxRelated)
|
||||
assert.Equal(t, qifExpenseTransaction, actualData2.CategoryType)
|
||||
|
||||
actualData3, err := reader.parseCategory(context, []string{
|
||||
"NCategory Name:Sub Category Name",
|
||||
@@ -460,9 +460,9 @@ func TestQifDataReaderParseCategory_SupportedFields(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Category Name:Sub Category Name", actualData3.name)
|
||||
assert.Equal(t, "Some Text", actualData3.description)
|
||||
assert.Equal(t, qifExpenseTransaction, actualData3.categoryType)
|
||||
assert.Equal(t, "Category Name:Sub Category Name", actualData3.Name)
|
||||
assert.Equal(t, "Some Text", actualData3.Description)
|
||||
assert.Equal(t, qifExpenseTransaction, actualData3.CategoryType)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParseClass_SupportedFields(t *testing.T) {
|
||||
@@ -475,8 +475,8 @@ func TestQifDataReaderParseClass_SupportedFields(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Class Name", actualData.name)
|
||||
assert.Equal(t, "Some Text", actualData.description)
|
||||
assert.Equal(t, "Class Name", actualData.Name)
|
||||
assert.Equal(t, "Some Text", actualData.Description)
|
||||
}
|
||||
|
||||
func TestQifDataReaderParse_UnsupportedFieldsOrValues(t *testing.T) {
|
||||
@@ -489,7 +489,7 @@ func TestQifDataReaderParse_UnsupportedFieldsOrValues(t *testing.T) {
|
||||
"",
|
||||
}, false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, qifClearedStatusUnreconciled, actualTransactionData.clearedStatus)
|
||||
assert.Equal(t, qifClearedStatusUnreconciled, actualTransactionData.ClearedStatus)
|
||||
|
||||
actualMemorizedTransactionData, err := reader.parseMemorizedTransaction(context, []string{
|
||||
"ZTest",
|
||||
@@ -497,7 +497,7 @@ func TestQifDataReaderParse_UnsupportedFieldsOrValues(t *testing.T) {
|
||||
"",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, qifInvalidTransactionType, actualMemorizedTransactionData.transactionType)
|
||||
assert.Equal(t, qifInvalidTransactionType, actualMemorizedTransactionData.TransactionType)
|
||||
|
||||
_, err = reader.parseInvestmentTransaction(context, []string{
|
||||
"ZTest",
|
||||
|
||||
@@ -119,11 +119,11 @@ func (t *qifTransactionDataRowIterator) Next(ctx core.Context, user *models.User
|
||||
func (t *qifTransactionDataRowIterator) parseTransaction(ctx core.Context, user *models.User, qifTransaction *qifTransactionData) (map[datatable.TransactionDataTableColumn]string, error) {
|
||||
data := make(map[datatable.TransactionDataTableColumn]string, len(qifTransactionSupportedColumns))
|
||||
|
||||
if qifTransaction.date == "" {
|
||||
if qifTransaction.Date == "" {
|
||||
return nil, errs.ErrMissingTransactionTime
|
||||
}
|
||||
|
||||
transactionTime, err := t.parseTransactionTime(ctx, qifTransaction.date)
|
||||
transactionTime, err := t.parseTransactionTime(ctx, qifTransaction.Date)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -131,37 +131,37 @@ func (t *qifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = transactionTime
|
||||
|
||||
if qifTransaction.amount == "" {
|
||||
if qifTransaction.Amount == "" {
|
||||
return nil, errs.ErrAmountInvalid
|
||||
}
|
||||
|
||||
amount, err := utils.ParseAmount(strings.ReplaceAll(qifTransaction.amount, ",", "")) // trim thousands separator
|
||||
amount, err := utils.ParseAmount(strings.ReplaceAll(qifTransaction.Amount, ",", "")) // trim thousands separator
|
||||
|
||||
if err != nil {
|
||||
return nil, errs.ErrAmountInvalid
|
||||
}
|
||||
|
||||
if qifTransaction.account != nil {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = qifTransaction.account.name
|
||||
if qifTransaction.Account != nil {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = qifTransaction.Account.Name
|
||||
} else {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = ""
|
||||
}
|
||||
|
||||
if len(qifTransaction.category) > 0 && qifTransaction.category[0] == '[' && qifTransaction.category[len(qifTransaction.category)-1] == ']' {
|
||||
if qifTransaction.payee == qifOpeningBalancePayeeText { // balance modification
|
||||
if len(qifTransaction.Category) > 0 && qifTransaction.Category[0] == '[' && qifTransaction.Category[len(qifTransaction.Category)-1] == ']' {
|
||||
if qifTransaction.Payee == qifOpeningBalancePayeeText { // balance modification
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = qifTransactionTypeNameMapping[models.TRANSACTION_TYPE_MODIFY_BALANCE]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(amount)
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = qifTransaction.category[1 : len(qifTransaction.category)-1]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = qifTransaction.Category[1 : len(qifTransaction.Category)-1]
|
||||
} else { // transfer
|
||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = qifTransactionTypeNameMapping[models.TRANSACTION_TYPE_TRANSFER]
|
||||
|
||||
if amount >= 0 { // transfer from [account name]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(amount)
|
||||
data[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME] = data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = qifTransaction.category[1 : len(qifTransaction.category)-1]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = qifTransaction.Category[1 : len(qifTransaction.Category)-1]
|
||||
} else { // transfer to [account name]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(-amount)
|
||||
data[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME] = qifTransaction.category[1 : len(qifTransaction.category)-1]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME] = qifTransaction.Category[1 : len(qifTransaction.Category)-1]
|
||||
}
|
||||
}
|
||||
} else { // income/expense
|
||||
@@ -173,20 +173,20 @@ func (t *qifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
||||
data[datatable.TRANSACTION_DATA_TABLE_AMOUNT] = utils.FormatAmount(-amount)
|
||||
}
|
||||
|
||||
if strings.Index(qifTransaction.category, ":") > 0 { // category:subcategory
|
||||
categories := strings.Split(qifTransaction.category, ":")
|
||||
if strings.Index(qifTransaction.Category, ":") > 0 { // category:subcategory
|
||||
categories := strings.Split(qifTransaction.Category, ":")
|
||||
data[datatable.TRANSACTION_DATA_TABLE_CATEGORY] = categories[0]
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = categories[len(categories)-1]
|
||||
} else {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_CATEGORY] = ""
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = qifTransaction.category
|
||||
data[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = qifTransaction.Category
|
||||
}
|
||||
}
|
||||
|
||||
if qifTransaction.memo != "" {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = qifTransaction.memo
|
||||
} else if qifTransaction.payee != "" && qifTransaction.payee != qifOpeningBalancePayeeText {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = qifTransaction.payee
|
||||
if qifTransaction.Memo != "" {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = qifTransaction.Memo
|
||||
} else if qifTransaction.Payee != "" && qifTransaction.Payee != qifOpeningBalancePayeeText {
|
||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = qifTransaction.Payee
|
||||
}
|
||||
|
||||
return data, nil
|
||||
@@ -240,11 +240,11 @@ func createNewQifTransactionDataTable(dateFormatType qifDateFormatType, qifData
|
||||
}
|
||||
|
||||
allData := make([]*qifTransactionData, 0)
|
||||
allData = append(allData, qifData.bankAccountTransactions...)
|
||||
allData = append(allData, qifData.cashAccountTransactions...)
|
||||
allData = append(allData, qifData.creditCardAccountTransactions...)
|
||||
allData = append(allData, qifData.assetAccountTransactions...)
|
||||
allData = append(allData, qifData.liabilityAccountTransactions...)
|
||||
allData = append(allData, qifData.BankAccountTransactions...)
|
||||
allData = append(allData, qifData.CashAccountTransactions...)
|
||||
allData = append(allData, qifData.CreditCardAccountTransactions...)
|
||||
allData = append(allData, qifData.AssetAccountTransactions...)
|
||||
allData = append(allData, qifData.LiabilityAccountTransactions...)
|
||||
|
||||
return &qifTransactionDataTable{
|
||||
dateFormatType: dateFormatType,
|
||||
|
||||
Reference in New Issue
Block a user