summary history files

internal/merge/ofxfiles.go
package merge

import (
	"fmt"
	"time"

	"github.com/aclindsa/ofxgo"
)

type ofxFiles struct {
	statementType statementType
	currSymbol    ofxgo.CurrSymbol
	bankAccount   *ofxgo.BankAcct
	ccAccount     *ofxgo.CCAcct
	files         []ofxFile
}

func (o ofxFiles) validate(resp *ofxgo.Response) error {
	if len(o.files) == 0 {
		return nil
	}

	respType, err := getStatementType(resp)
	if err != nil {
		return err
	}

	if respType != o.statementType {
		return fmt.Errorf("statement is different type to previous statement (%v, %v)", respType, o.statementType)
	}

	return nil
}

// signonResponse ranges through ofxFiles returning the latest signonResponse.
// If no signonResponse is found or the dtServer timestamp is not set within
// the signonResponses, a default signonResponse is returned.
func (o ofxFiles) signonResponse() ofxgo.SignonResponse {
	s := ofxgo.SignonResponse{
		Language: ofxgo.String("ENG"),
		Status: ofxgo.Status{
			Code:     ofxgo.Int(0),
			Severity: ofxgo.String("INFO"),
		},
	}

	dtServer := ofxgo.Date{}
	for _, ofxFile := range o.files {
		if ofxFile.resp == nil {
			continue
		}
		if dtServer.Time.IsZero() {
			s = ofxFile.resp.Signon
		}
		if ofxFile.resp.Signon.DtServer.Time.After(dtServer.Time) {
			s = ofxFile.resp.Signon
			continue
		}
	}

	return s
}

func (o ofxFiles) dtAsOf() ofxgo.Date {
	d := ofxgo.Date{}
	for _, ofxFile := range o.files {
		if ofxFile.resp == nil {
			continue
		}
		switch o.statementType {
		case statementTypeBank:
			if len(ofxFile.resp.Bank) == 0 {
				continue
			}
			stmt, ok := ofxFile.resp.Bank[0].(*ofxgo.StatementResponse)
			if !ok {
				continue
			}
			if d.Time.IsZero() {
				d = stmt.DtAsOf
				continue
			}
			if stmt.DtAsOf.Time.Before(d.Time) {
				d = stmt.DtAsOf
				continue
			}
		case statementTypeCreditCard:
			if len(ofxFile.resp.CreditCard) == 0 {
				continue
			}
			stmt, ok := ofxFile.resp.CreditCard[0].(*ofxgo.StatementResponse)
			if !ok {
				continue
			}
			if d.Time.IsZero() {
				d = stmt.DtAsOf
				continue
			}
			if stmt.DtAsOf.Time.Before(d.Time) {
				d = stmt.DtAsOf
				continue
			}
		}
	}

	if d.Time.IsZero() {
		now := time.Now()
		return *ofxgo.NewDate(
			now.Year(),
			now.Month(),
			now.Day(),
			now.Hour(),
			now.Minute(),
			now.Second(),
			now.Nanosecond(),
			now.Location(),
		)
	}
	return d
}

func (o ofxFiles) dtStart() ofxgo.Date {
	d := time.Time{}
	for _, ofxFile := range o.files {
		switch o.statementType {
		case statementTypeBank:
			if len(ofxFile.resp.Bank) == 0 {
				continue
			}
			stmt, ok := ofxFile.resp.Bank[0].(*ofxgo.StatementResponse)
			if !ok {
				continue
			}
			if stmt.BankTranList == nil {
				continue
			}
			if d.IsZero() {
				d = stmt.BankTranList.DtStart.Time
				continue
			}
			if stmt.BankTranList.DtStart.Time.After(d) {
				d = stmt.BankTranList.DtStart.Time
				continue
			}
		case statementTypeCreditCard:
			if len(ofxFile.resp.CreditCard) == 0 {
				continue
			}
			stmt, ok := ofxFile.resp.CreditCard[0].(*ofxgo.CCStatementResponse)
			if !ok {
				continue
			}
			if stmt.BankTranList == nil {
				continue
			}
			if d.IsZero() {
				d = stmt.BankTranList.DtStart.Time
				continue
			}
			if stmt.BankTranList.DtStart.Time.After(d) {
				d = stmt.BankTranList.DtStart.Time
				continue
			}
		}
	}
	if d.IsZero() {
		d = time.Now()
	}
	return *ofxgo.NewDate(
		d.Year(),
		d.Month(),
		d.Day(),
		d.Hour(),
		d.Minute(),
		d.Second(),
		d.Nanosecond(),
		d.Location(),
	)
}

func (o ofxFiles) dtEnd() ofxgo.Date {
	d := time.Time{}
	for _, ofxFile := range o.files {
		switch o.statementType {
		case statementTypeBank:
			if len(ofxFile.resp.Bank) == 0 {
				continue
			}
			stmt, ok := ofxFile.resp.Bank[0].(*ofxgo.StatementResponse)
			if !ok {
				continue
			}
			if stmt.BankTranList == nil {
				continue
			}
			if d.IsZero() {
				d = stmt.BankTranList.DtEnd.Time
				continue
			}
			if stmt.BankTranList.DtEnd.Time.Before(d) {
				d = stmt.BankTranList.DtEnd.Time
				continue
			}
		case statementTypeCreditCard:
			if len(ofxFile.resp.Bank) == 0 {
				continue
			}
			stmt, ok := ofxFile.resp.Bank[0].(*ofxgo.StatementResponse)
			if !ok {
				continue
			}
			if stmt.BankTranList == nil {
				continue
			}
			if d.IsZero() {
				d = stmt.BankTranList.DtEnd.Time
				continue
			}
			if stmt.BankTranList.DtEnd.Time.Before(d) {
				d = stmt.BankTranList.DtEnd.Time
				continue
			}
		}
	}
	if d.IsZero() {
		d = time.Now()
	}
	return *ofxgo.NewDate(
		d.Year(),
		d.Month(),
		d.Day(),
		d.Hour(),
		d.Minute(),
		d.Second(),
		d.Nanosecond(),
		d.Location(),
	)
}

type ofxFile struct {
	resp *ofxgo.Response
}