Browse Source

basic transaction check to make sure it balances prior to commit

bmallred 10 years ago
parent
commit
3a259ebfae
4 changed files with 108 additions and 11 deletions
  1. 87 9
      commit.go
  2. 1 1
      credit.go
  3. 1 1
      debit.go
  4. 19 0
      general.ledger

+ 87 - 9
commit.go

@ -4,6 +4,7 @@ import (
4 4
	"errors"
5 5
	"fmt"
6 6
	"io/ioutil"
7
	"math/big"
7 8
	"os"
8 9
	"strings"
9 10
	"time"
@ -34,11 +35,11 @@ func actionCommit(c *cli.Context) {
34 35
	project := parseProject(args)
35 36
	description := parseDescription(args, project)
36 37
37
	writeTransaction(date, project, description)
38
	writeTransaction(date.Format("2006-01-02"), project, description)
38 39
}
39 40
40 41
// Parse the given string to extract a proper date
41
func parseDate(in string) (string, error) {
42
func parseDate(in string) (time.Time, error) {
42 43
	formats := []string{
43 44
		"2006-01-02",
44 45
		"2006/01/02",
@ -57,11 +58,11 @@ func parseDate(in string) (string, error) {
57 58
	for _, f := range formats {
58 59
		d, err := time.Parse(f, in)
59 60
		if err == nil {
60
			return d.Format(formats[0]), nil
61
			return d, nil
61 62
		}
62 63
	}
63 64
64
	return "", errors.New("No valid date provided")
65
	return time.Now().UTC(), errors.New("No valid date provided")
65 66
}
66 67
67 68
// Parse a given string to extract a project name
@ -90,6 +91,81 @@ func parseDescription(fields []string, project string) string {
90 91
	return strings.Replace(strings.Join(fields, " "), "  ", " ", -1)
91 92
}
92 93
94
type Account struct {
95
	Name   string
96
	Debit  bool
97
	Amount *big.Rat
98
}
99
100
type Transaction struct {
101
	Date        time.Time
102
	Project     string
103
	Description string
104
	Accounts    []Account
105
}
106
107
func (t *Transaction) FromString(text string) error {
108
	// Parse the lines of text
109
	lines := strings.Split(text, "\n")
110
	for i, line := range lines {
111
		fields := strings.Split(line, "\t")
112
113
		switch i {
114
		case 0:
115
			date, err := parseDate(fields[0])
116
			check(err)
117
			project := fields[1]
118
			description := ""
119
			if len(fields) > 2 {
120
				description = strings.Join(fields[2:], " ")
121
			}
122
123
			t = &Transaction{
124
				Date:        date,
125
				Project:     project,
126
				Description: description,
127
				Accounts:    []Account{},
128
			}
129
			break
130
131
		default:
132
			if len(fields) != 3 {
133
				break
134
			}
135
136
			account := fields[0]
137
			debit := true
138
139
			if strings.HasPrefix(fields[1], "-") {
140
				debit = false
141
			}
142
			value := new(big.Rat)
143
			value.SetString(fields[1][1:])
144
145
			t.Accounts = append(
146
				t.Accounts,
147
				Account{
148
					Name:   account,
149
					Debit:  debit,
150
					Amount: value,
151
				})
152
153
			break
154
		}
155
	}
156
157
	// Check that they balance
158
	balance := new(big.Rat)
159
	for _, a := range t.Accounts {
160
		balance.Add(balance, a.Amount)
161
	}
162
	if balance.FloatString(2) != "0.00" {
163
		return errors.New("Transaction does not balance")
164
	}
165
166
	return nil
167
}
168
93 169
// Write a transaction line where there is a pending transaction
94 170
func writeTransaction(date, project, description string) {
95 171
	if !hasPendingTransaction() {
@ -99,13 +175,15 @@ func writeTransaction(date, project, description string) {
99 175
	pending, err := ioutil.ReadFile(PendingFile)
100 176
	check(err)
101 177
178
	// Find the line containing @pending and replace it with our transaction
179
	s := fmt.Sprintf("%s\t%s\t%s\n%s\n", date, project, description, string(pending))
180
	var t Transaction
181
	err = t.FromString(s)
182
	check(err)
183
102 184
	file, err := os.OpenFile(Ledger, os.O_APPEND|os.O_WRONLY, 0666)
103 185
	check(err)
104 186
	defer file.Close()
105
106
	// Find the line containing @pending and replace it with our transaction
107
	transaction := fmt.Sprintf("%s\t%s\t%s\n%s\n", date, project, description, pending)
108
109
	_, err = file.WriteString(transaction)
187
	_, err = file.WriteString(s)
110 188
	check(err)
111 189
}

+ 1 - 1
credit.go

@ -28,6 +28,6 @@ func actionCredit(c *cli.Context) {
28 28
	check(err)
29 29
	defer f.Close()
30 30
31
	_, err = f.WriteString(fmt.Sprintf("\t%s\t+%s\n", account, value.FloatString(2)))
31
	_, err = f.WriteString(fmt.Sprintf("\t%s\t-%s\n", account, value.FloatString(2)))
32 32
	check(err)
33 33
}

+ 1 - 1
debit.go

@ -28,6 +28,6 @@ func actionDebit(c *cli.Context) {
28 28
	check(err)
29 29
	defer f.Close()
30 30
31
	_, err = f.WriteString(fmt.Sprintf("\t%s\t-%s\n", account, value.FloatString(2)))
31
	_, err = f.WriteString(fmt.Sprintf("\t%s\t+%s\n", account, value.FloatString(2)))
32 32
	check(err)
33 33
}

+ 19 - 0
general.ledger

@ -0,0 +1,19 @@
1
2015-01-03  @general    Some other stuff
2
    cash       -10.00
3
    services   +10.00
4
5
2015-01-03  @general    Multiple accounts example
6
    cash       +10.00
7
    liability  -5.00
8
    a          -5.00
9
10
2015-01-05	@general	blah
11
	savings	-10.00
12
	cash	+5.00
13
	sheep	+5.00
14
15
2015-01-05	@general	blah
16
	savings	-10.00
17
	cash	+5.00
18
	sheep	+5.00
19