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
	"errors"
4
	"errors"
5
	"fmt"
5
	"fmt"
6
	"io/ioutil"
6
	"io/ioutil"
7
	"math/big"
7
	"os"
8
	"os"
8
	"strings"
9
	"strings"
9
	"time"
10
	"time"
34
	project := parseProject(args)
35
	project := parseProject(args)
35
	description := parseDescription(args, project)
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
// Parse the given string to extract a proper date
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
	formats := []string{
43
	formats := []string{
43
		"2006-01-02",
44
		"2006-01-02",
44
		"2006/01/02",
45
		"2006/01/02",
57
	for _, f := range formats {
58
	for _, f := range formats {
58
		d, err := time.Parse(f, in)
59
		d, err := time.Parse(f, in)
59
		if err == nil {
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
// Parse a given string to extract a project name
68
// Parse a given string to extract a project name
90
	return strings.Replace(strings.Join(fields, " "), "  ", " ", -1)
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
// Write a transaction line where there is a pending transaction
169
// Write a transaction line where there is a pending transaction
94
func writeTransaction(date, project, description string) {
170
func writeTransaction(date, project, description string) {
95
	if !hasPendingTransaction() {
171
	if !hasPendingTransaction() {
99
	pending, err := ioutil.ReadFile(PendingFile)
175
	pending, err := ioutil.ReadFile(PendingFile)
100
	check(err)
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
	file, err := os.OpenFile(Ledger, os.O_APPEND|os.O_WRONLY, 0666)
184
	file, err := os.OpenFile(Ledger, os.O_APPEND|os.O_WRONLY, 0666)
103
	check(err)
185
	check(err)
104
	defer file.Close()
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
	check(err)
188
	check(err)
111
}
189
}

+ 1 - 1
credit.go

28
	check(err)
28
	check(err)
29
	defer f.Close()
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
	check(err)
32
	check(err)
33
}
33
}

+ 1 - 1
debit.go

28
	check(err)
28
	check(err)
29
	defer f.Close()
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
	check(err)
32
	check(err)
33
}
33
}

+ 19 - 0
general.ledger

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