Browse Source

created new types and some refactoring

bmallred 10 years ago
parent
commit
20b2ccd66d
7 changed files with 163 additions and 118 deletions
  1. 1 0
      .gitignore
  2. 45 0
      account.go
  3. 8 4
      cash.go
  4. 21 93
      commit.go
  5. 0 7
      example.ledger
  6. 0 14
      general.ledger
  7. 88 0
      transaction.go

+ 1 - 0
.gitignore

@ -21,3 +21,4 @@ _testmain.go
21 21
22 22
*.exe
23 23
cash
24
*.ledger

+ 45 - 0
account.go

@ -0,0 +1,45 @@
1
package main
2
3
import (
4
	"errors"
5
	"fmt"
6
	"math/big"
7
	"strings"
8
)
9
10
type Account struct {
11
	Name   string
12
	Debit  bool
13
	Amount *big.Rat
14
}
15
16
// Convert from a string to an account
17
func (a *Account) FromString(text string) error {
18
	fields := strings.Split(text, "\t")
19
20
	if len(fields) != 3 {
21
		return errors.New("Invalid account format")
22
	}
23
24
	debit := true
25
	if strings.HasPrefix(fields[2], "-") {
26
		debit = false
27
	}
28
29
	a.Debit = debit
30
	a.Name = fields[1]
31
	a.Amount = new(big.Rat)
32
	a.Amount.SetString(fields[2][1:])
33
34
	return nil
35
}
36
37
// Convert the account to string format
38
func (a *Account) ToString() string {
39
	symbol := "-"
40
	if a.Debit {
41
		symbol = "+"
42
	}
43
44
	return fmt.Sprintf("\t%s\t%s%s\n", a.Name, symbol, a.Amount.FloatString(2))
45
}

+ 8 - 4
cash.go

@ -15,14 +15,18 @@ const (
15 15
)
16 16
17 17
var (
18
	Ledger            = "general.ledger"
19
	TransactionFormat = "%s\t%s\t%s"
20
	AccountFormat     = "\t%s\t%s"
21
	PendingFile       = filepath.Join(os.TempDir(), "pending.ledger")
18
	LedgerFile  = "general.ledger"
19
	PendingFile = filepath.Join(os.TempDir(), "pending.ledger")
22 20
)
23 21
24 22
// Initialize the application
25 23
func init() {
24
	if _, err := os.Stat(LedgerFile); os.IsNotExist(err) {
25
		_, err = os.Create(LedgerFile)
26
		check(err)
27
		log.Println("Created ledger file at", LedgerFile)
28
	}
29
26 30
	if _, err := os.Stat(PendingFile); os.IsNotExist(err) {
27 31
		_, err = os.Create(PendingFile)
28 32
		check(err)

+ 21 - 93
commit.go

@ -2,9 +2,7 @@ package main
2 2
3 3
import (
4 4
	"errors"
5
	"fmt"
6 5
	"io/ioutil"
7
	"math/big"
8 6
	"os"
9 7
	"strings"
10 8
	"time"
@ -35,7 +33,7 @@ func actionCommit(c *cli.Context) {
35 33
	project := parseProject(args)
36 34
	description := parseDescription(args, project)
37 35
38
	writeTransaction(date.Format("2006-01-02"), project, description)
36
	writeTransaction(date, project, description)
39 37
}
40 38
41 39
// Parse the given string to extract a proper date
@ -91,91 +89,8 @@ func parseDescription(fields []string, project string) string {
91 89
	return strings.Replace(strings.Join(fields, " "), "  ", " ", -1)
92 90
}
93 91
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[1]
137
			debit := true
138
139
			if strings.HasPrefix(fields[2], "-") {
140
				debit = false
141
			}
142
			value := new(big.Rat)
143
			value.SetString(fields[2][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
	if len(t.Accounts) == 0 {
158
		return errors.New("Transaction does not have any accounts")
159
	}
160
161
	// Check that they balance
162
	balance := new(big.Rat)
163
	for _, a := range t.Accounts {
164
		if a.Debit {
165
			balance.Add(balance, a.Amount)
166
		} else {
167
			balance.Sub(balance, a.Amount)
168
		}
169
	}
170
	if balance.FloatString(2) != "0.00" {
171
		return errors.New("Transaction does not balance")
172
	}
173
174
	return nil
175
}
176
177 92
// Write a transaction line where there is a pending transaction
178
func writeTransaction(date, project, description string) {
93
func writeTransaction(date time.Time, project, description string) {
179 94
	if !hasPendingTransaction() {
180 95
		check(errors.New("No pending transaction to write"))
181 96
	}
@ -183,15 +98,28 @@ func writeTransaction(date, project, description string) {
183 98
	pending, err := ioutil.ReadFile(PendingFile)
184 99
	check(err)
185 100
186
	// Find the line containing @pending and replace it with our transaction
187
	s := fmt.Sprintf("%s\t%s\t%s\n%s\n", date, project, description, string(pending))
188
	var t Transaction
189
	err = t.FromString(s)
101
	t := Transaction{
102
		Date:        date,
103
		Project:     project,
104
		Description: description,
105
		Accounts:    []Account{},
106
	}
107
108
	lines := strings.Split(strings.TrimRight(string(pending), "\n"), "\n")
109
	for _, line := range lines {
110
		var a Account
111
		err = a.FromString(line)
112
		check(err)
113
114
		t.Accounts = append(t.Accounts, a)
115
	}
116
117
	err = t.CheckBalance()
190 118
	check(err)
191 119
192
	file, err := os.OpenFile(Ledger, os.O_APPEND|os.O_WRONLY, 0666)
120
	file, err := os.OpenFile(LedgerFile, os.O_APPEND|os.O_WRONLY, 0666)
193 121
	check(err)
194 122
	defer file.Close()
195
	_, err = file.WriteString(s)
123
	_, err = file.WriteString(t.ToString())
196 124
	check(err)
197 125
}

+ 0 - 7
example.ledger

@ -1,7 +0,0 @@
1
2015-01-03  @general    Some other stuff
2
    #cash       -10.00
3
    #services   +10.00
4
2015-01-03  @general    Multiple accounts example
5
    #cash       +10.00
6
    #liability  -5.00
7
    #a          -5.00

+ 0 - 14
general.ledger

@ -1,14 +0,0 @@
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

+ 88 - 0
transaction.go

@ -0,0 +1,88 @@
1
package main
2
3
import (
4
	"errors"
5
	"fmt"
6
	"math/big"
7
	"strings"
8
	"time"
9
)
10
11
type Transaction struct {
12
	Date        time.Time
13
	Project     string
14
	Description string
15
	Accounts    []Account
16
}
17
18
// Create a transaction from a string
19
func (t *Transaction) FromString(text string) {
20
	// Parse the lines of text
21
	lines := strings.Split(text, "\n")
22
	for i, line := range lines {
23
		switch i {
24
		case 0:
25
			fields := strings.Split(line, "\t")
26
27
			date, err := parseDate(fields[0])
28
			check(err)
29
30
			project := fields[1]
31
32
			description := ""
33
			if len(fields) > 2 {
34
				description = strings.Join(fields[2:], " ")
35
			}
36
37
			t = &Transaction{
38
				Date:        date,
39
				Project:     project,
40
				Description: description,
41
				Accounts:    []Account{},
42
			}
43
			break
44
45
		default:
46
			var account Account
47
			err := account.FromString(line)
48
			check(err)
49
50
			t.Accounts = append(t.Accounts, account)
51
			break
52
		}
53
	}
54
}
55
56
// Check the transaction to ensure it is balanced
57
func (t *Transaction) CheckBalance() error {
58
	if len(t.Accounts) == 0 {
59
		return errors.New("Transaction does not have any accounts")
60
	}
61
62
	// Check that they balance
63
	balance := new(big.Rat)
64
	for _, a := range t.Accounts {
65
		if a.Debit {
66
			balance.Add(balance, a.Amount)
67
		} else {
68
			balance.Sub(balance, a.Amount)
69
		}
70
	}
71
72
	if balance.FloatString(2) != "0.00" {
73
		return errors.New("Transaction does not balance")
74
	}
75
76
	return nil
77
}
78
79
// Convert a transaction to string format
80
func (t *Transaction) ToString() string {
81
	accounts := ""
82
83
	for _, account := range t.Accounts {
84
		accounts += account.ToString()
85
	}
86
87
	return fmt.Sprintf("%s\t%s\t%s\n%s\n", t.Date.Format("2006-01-02"), t.Project, t.Description, string(accounts))
88
}