aboutsummaryrefslogtreecommitdiffstats
path: root/record/morrow/morrow.go
blob: 0a8ae0ccd8280fb8d2904bd788bbc93c12e0dc9a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package morrow

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/mpolden/journal/record"
)

// Reader implements a reader for Morrow-encoded (CSV) records.
type Reader struct {
	rd io.Reader
}

// NewReader returns a new reader for Morrow-encoded records.
func NewReader(rd io.Reader) *Reader {
	return &Reader{
		rd: rd,
	}
}

// Read all records from the underlying reader.
func (r *Reader) Read() ([]record.Record, error) {
	buf := bufio.NewReader(r.rd)
	c := csv.NewReader(buf)
	c.FieldsPerRecord = -1 // Morrow export has an additional field which is not in the header
	var rs []record.Record
	line := 0
	for {
		csvRecord, err := c.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		line++
		if len(csvRecord) < 10 {
			continue
		}
		if line == 1 {
			continue // Skip header
		}
		t, err := time.Parse("02.01.2006", csvRecord[0])
		if err != nil {
			return nil, fmt.Errorf("invalid time on line %d: %q: %w", line, csvRecord[0], err)
		}
		amount, err := parseAmount(csvRecord[5])
		if err != nil {
			return nil, fmt.Errorf("invalid amount on line %d: %q: %w", line, amount, err)
		}
		text := strings.TrimSpace(csvRecord[2])
		rs = append(rs, record.Record{Time: t, Text: text, Amount: amount})
	}
	return rs, nil
}

func parseAmount(s string) (int64, error) {
	v := strings.ReplaceAll(s, ".", "")
	n, err := strconv.ParseInt(v, 10, 64)
	if err != nil {
		return 0, err
	}
	return n, nil
}