statements 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +22 -0
- data/bin/statements +8 -0
- data/lib/html/css/main.css +115 -0
- data/lib/html/css/main.css.map +7 -0
- data/lib/html/css/main.scss +173 -0
- data/lib/html/index.html +94 -0
- data/lib/html/js/main.coffee +146 -0
- data/lib/html/js/main.js +174 -0
- data/lib/html/js/main.js.map +10 -0
- data/lib/html/vendor/bootstrap/css/bootstrap-theme.css +469 -0
- data/lib/html/vendor/bootstrap/css/bootstrap.css +6331 -0
- data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +229 -0
- data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/html/vendor/bootstrap/js/bootstrap.js +2320 -0
- data/lib/html/vendor/bootstrap-datepicker.js +1681 -0
- data/lib/html/vendor/datepicker3.css +786 -0
- data/lib/html/vendor/jquery-2.1.3.js +9205 -0
- data/lib/html/vendor/underscore.js +1416 -0
- data/lib/statements/cli.rb +17 -0
- data/lib/statements/database.rb +22 -0
- data/lib/statements/migrations/00_alpha.rb +43 -0
- data/lib/statements/models/account.rb +9 -0
- data/lib/statements/models/document.rb +29 -0
- data/lib/statements/models/transaction.rb +43 -0
- data/lib/statements/pdf_reader.rb +34 -0
- data/lib/statements/reader/common/st_george.rb +31 -0
- data/lib/statements/reader/st_george_credit_card.rb +53 -0
- data/lib/statements/reader/st_george_savings.rb +95 -0
- data/lib/statements/reader.rb +76 -0
- data/lib/statements/search.rb +48 -0
- data/lib/statements/server.rb +69 -0
- data/lib/statements/version.rb +3 -0
- data/lib/statements/views/footer.erb +6 -0
- data/lib/statements/views/row.erb +16 -0
- data/lib/statements/views/search.erb +25 -0
- data/lib/statements.rb +17 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ff2b7e3e918329fc0e03bec9e77b195c3843fda6
|
4
|
+
data.tar.gz: 42b4173c2c664d3e510d90946875b7ea0a028772
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e4050e7a812a5e0fa47ffd4441f9949e96f78b866b3aadf69547345bc0455c0b70671371e35e68a4b98f293be67874e2d8f956a8cbf8000cc341b01b157ed7b
|
7
|
+
data.tar.gz: 3f5c9a2c5abe973b48468fa660623c8d66de92f987c6168322cbd6ccaf41d163ee3df2a394bca7526ab7781a69b32d9f4fb91a7337ed203d0f3f24cb4543562a
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Neil E. Pearson
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Statements
|
2
|
+
|
3
|
+
Builds a database of bank account transaction history by reading PDF bank statements, and provides a simple web interface for browsing data.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```bash
|
8
|
+
$ gem install statements
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
1. `cd` to a directory containing all your bank statements.
|
14
|
+
2. Run `statements` and wait for new statements to be parsed.
|
15
|
+
3. Open `http://localhost:57473` in a browser.
|
16
|
+
|
17
|
+
## Supported banks
|
18
|
+
|
19
|
+
- St. George Credit Cards
|
20
|
+
- St. George Cash Accounts
|
21
|
+
|
22
|
+
The list is small. Want it to grow? Send a pull request!
|
data/bin/statements
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
.container-fluid {
|
2
|
+
padding-top: 15px; }
|
3
|
+
|
4
|
+
#colour a:before {
|
5
|
+
content: "";
|
6
|
+
border: 1px solid transparent;
|
7
|
+
padding: 6px;
|
8
|
+
display: block;
|
9
|
+
border-radius: 15px; }
|
10
|
+
#colour a.colour-white:before {
|
11
|
+
background: white; }
|
12
|
+
#colour a.colour-red:before {
|
13
|
+
background: #a13f3e; }
|
14
|
+
#colour a.colour-yellow:before {
|
15
|
+
background: #e9d543; }
|
16
|
+
#colour a.colour-green:before {
|
17
|
+
background: #3f9d5a; }
|
18
|
+
#colour a.colour-cyan:before {
|
19
|
+
background: #99d7ff; }
|
20
|
+
#colour a.colour-blue:before {
|
21
|
+
background: #48609d; }
|
22
|
+
#colour a.colour-magenta:before {
|
23
|
+
background: #af6bd7; }
|
24
|
+
|
25
|
+
#content thead th {
|
26
|
+
padding-top: 0; }
|
27
|
+
#content td p {
|
28
|
+
margin: 0; }
|
29
|
+
#content td p.small {
|
30
|
+
color: #666; }
|
31
|
+
#content .amount,
|
32
|
+
#content tfoot tr > * {
|
33
|
+
text-align: right; }
|
34
|
+
#content .credit .amount:before {
|
35
|
+
content: "($"; }
|
36
|
+
#content .credit .amount:after {
|
37
|
+
content: ")"; }
|
38
|
+
#content .debit .amount:before,
|
39
|
+
#content tfoot td:before {
|
40
|
+
content: "$"; }
|
41
|
+
#content tr.colour-white {
|
42
|
+
background: white; }
|
43
|
+
#content tr.colour-red {
|
44
|
+
background: #ffefef; }
|
45
|
+
#content tr.colour-yellow {
|
46
|
+
background: #ffffd7; }
|
47
|
+
#content tr.colour-green {
|
48
|
+
background: #e9fce7; }
|
49
|
+
#content tr.colour-blue {
|
50
|
+
background: #ebeffd; }
|
51
|
+
#content tr.colour-cyan {
|
52
|
+
background: #e1f7f7; }
|
53
|
+
#content tr.colour-magenta {
|
54
|
+
background: #fbefff; }
|
55
|
+
#content tr > td,
|
56
|
+
#content tr > th {
|
57
|
+
background: inherit; }
|
58
|
+
#content tr.colour-white a.picker,
|
59
|
+
#content li.colour-white a {
|
60
|
+
background: white; }
|
61
|
+
#content tr.colour-red a.picker,
|
62
|
+
#content li.colour-red a {
|
63
|
+
background: #a13f3e; }
|
64
|
+
#content tr.colour-green a.picker,
|
65
|
+
#content li.colour-green a {
|
66
|
+
background: #3f9d5a; }
|
67
|
+
#content tr.colour-blue a.picker,
|
68
|
+
#content li.colour-blue a {
|
69
|
+
background: #48609d; }
|
70
|
+
#content tr.colour-cyan a.picker,
|
71
|
+
#content li.colour-cyan a {
|
72
|
+
background: #99d7ff; }
|
73
|
+
#content tr.colour-magenta a.picker,
|
74
|
+
#content li.colour-magenta a {
|
75
|
+
background: #af6bd7; }
|
76
|
+
#content tr.colour-yellow a.picker,
|
77
|
+
#content li.colour-yellow a {
|
78
|
+
background: #e9d543; }
|
79
|
+
#content td.colour {
|
80
|
+
width: 20px;
|
81
|
+
position: relative; }
|
82
|
+
#content td.colour a {
|
83
|
+
display: block;
|
84
|
+
border: 1px solid #eee;
|
85
|
+
border-radius: 10px;
|
86
|
+
width: 20px;
|
87
|
+
height: 20px; }
|
88
|
+
#content td.colour a:hover {
|
89
|
+
border-color: #ddd; }
|
90
|
+
#content td.colour ul {
|
91
|
+
list-style: none;
|
92
|
+
position: absolute;
|
93
|
+
display: block;
|
94
|
+
right: 100%;
|
95
|
+
top: 1px;
|
96
|
+
margin-bottom: 0;
|
97
|
+
width: 167px;
|
98
|
+
text-align: right;
|
99
|
+
background: white;
|
100
|
+
border: 1px solid #eee;
|
101
|
+
border-radius: 50px;
|
102
|
+
padding: 3px;
|
103
|
+
height: 28px; }
|
104
|
+
#content td.colour ul li {
|
105
|
+
display: inline-block;
|
106
|
+
margin-right: 3px; }
|
107
|
+
#content td.colour ul li:last-child {
|
108
|
+
margin-right: 0; }
|
109
|
+
|
110
|
+
footer {
|
111
|
+
font-size: .8em;
|
112
|
+
text-align: center;
|
113
|
+
padding: 20px 0; }
|
114
|
+
|
115
|
+
/*# sourceMappingURL=main.css.map */
|
@@ -0,0 +1,7 @@
|
|
1
|
+
{
|
2
|
+
"version": 3,
|
3
|
+
"mappings": "AASA,gBAAiB;EACf,WAAW,EAAE,IAAI;;AAKf,gBAAS;EACP,OAAO,EAAE,EAAE;EACX,MAAM,EAAE,qBAAqB;EAC7B,OAAO,EAAC,GAAG;EACX,OAAO,EAAE,KAAK;EACd,aAAa,EAAE,IAAI;AAErB,6BAAsB;EAAE,UAAU,EArB9B,KAAK;AAsBT,2BAAoB;EAAE,UAAU,EArB9B,OAAO;AAsBT,8BAAuB;EAAE,UAAU,EArB9B,OAAO;AAsBZ,6BAAsB;EAAE,UAAU,EArB9B,OAAO;AAsBX,4BAAqB;EAAE,UAAU,EApB9B,OAAO;AAqBV,4BAAqB;EAAE,UAAU,EAtB9B,OAAO;AAuBV,+BAAwB;EAAE,UAAU,EArB9B,OAAO;;AA2Bf,iBAAS;EACP,WAAW,EAAE,CAAC;AAGhB,aAAK;EACH,MAAM,EAAE,CAAC;EAET,mBAAQ;IACN,KAAK,EAAE,IAAI;AAIf;qBACa;EACX,UAAU,EAAE,KAAK;AAIjB,+BAAS;EAAE,OAAO,EAAE,IAAI;AACxB,8BAAQ;EAAE,OAAO,EAAE,GAAG;AAKtB;wBAAS;EAAE,OAAO,EAAE,GAAG;AAIvB,wBAAe;EACb,UAAU,EA9DkB,KAAK;AAgEnC,sBAAa;EACX,UAAU,EAhEgB,OAAO;AAkEnC,yBAAgB;EACd,UAAU,EAlEmB,OAAO;AAoEtC,wBAAe;EACb,UAAU,EApEkB,OAAO;AAsErC,uBAAc;EACZ,UAAU,EAtEiB,OAAO;AAwEpC,uBAAc;EACZ,UAAU,EAxEiB,OAAO;AA0EpC,0BAAiB;EACf,UAAU,EA1EoB,OAAO;AA4EvC;gBACK;EACH,UAAU,EAAE,OAAO;AAIvB;0BACkB;EAChB,UAAU,EA1FN,KAAK;AA6FX;wBACgB;EACd,UAAU,EA9FR,OAAO;AAiGX;0BACkB;EAChB,UAAU,EAjGN,OAAO;AAoGb;yBACiB;EACf,UAAU,EArGP,OAAO;AAwGZ;yBACiB;EACf,UAAU,EAzGP,OAAO;AA4GZ;4BACoB;EAClB,UAAU,EA7GJ,OAAO;AAgHf;2BACmB;EACjB,UAAU,EAtHL,OAAO;AAyHd,kBAAU;EACR,KAAK,EAAE,IAAI;EACX,QAAQ,EAAE,QAAQ;EAElB,oBAAE;IACA,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,cAAc;IACtB,aAAa,EAAE,IAAI;IACnB,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IAEZ,0BAAQ;MACN,YAAY,EAAE,IAAI;EAItB,qBAAG;IACD,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,IAAI;IACX,GAAG,EAAE,GAAG;IACR,aAAa,EAAE,CAAC;IAChB,KAAK,EAAE,KAAK;IACZ,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,KAAK;IACjB,MAAM,EAAE,cAAc;IACtB,aAAa,EAAE,IAAI;IACnB,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,IAAI;IAEZ,wBAAG;MACD,OAAO,EAAE,YAAY;MACrB,YAAY,EAAE,GAAG;MAEjB,mCAAa;QACX,YAAY,EAAE,CAAC;;AAQzB,MAAO;EACL,SAAS,EAAE,IAAI;EACf,UAAU,EAAE,MAAM;EAClB,OAAO,EAAE,MAAM",
|
4
|
+
"sources": ["main.scss"],
|
5
|
+
"names": [],
|
6
|
+
"file": "main.css"
|
7
|
+
}
|
@@ -0,0 +1,173 @@
|
|
1
|
+
// Colours
|
2
|
+
$white: white; $light-white: white;
|
3
|
+
$red: #a13f3e; $light-red: #ffefef;
|
4
|
+
$yellow: #e9d543; $light-yellow: #ffffd7;
|
5
|
+
$green: #3f9d5a; $light-green: #e9fce7;
|
6
|
+
$blue: #48609d; $light-blue: #ebeffd;
|
7
|
+
$cyan: #99d7ff; $light-cyan: #e1f7f7;
|
8
|
+
$magenta: #af6bd7; $light-magenta: #fbefff;
|
9
|
+
|
10
|
+
.container-fluid {
|
11
|
+
padding-top: 15px;
|
12
|
+
}
|
13
|
+
|
14
|
+
#colour {
|
15
|
+
a {
|
16
|
+
&:before {
|
17
|
+
content: "";
|
18
|
+
border: 1px solid transparent;
|
19
|
+
padding:6px;
|
20
|
+
display: block;
|
21
|
+
border-radius: 15px;
|
22
|
+
}
|
23
|
+
&.colour-white:before { background: $white; }
|
24
|
+
&.colour-red:before { background: $red; }
|
25
|
+
&.colour-yellow:before { background: $yellow; }
|
26
|
+
&.colour-green:before { background: $green; }
|
27
|
+
&.colour-cyan:before { background: $cyan; }
|
28
|
+
&.colour-blue:before { background: $blue; }
|
29
|
+
&.colour-magenta:before { background: $magenta; }
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
#content {
|
34
|
+
|
35
|
+
thead th {
|
36
|
+
padding-top: 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
td p {
|
40
|
+
margin: 0;
|
41
|
+
|
42
|
+
&.small {
|
43
|
+
color: #666;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
.amount,
|
48
|
+
tfoot tr > * {
|
49
|
+
text-align: right;
|
50
|
+
}
|
51
|
+
|
52
|
+
.credit .amount {
|
53
|
+
&:before { content: "($"; }
|
54
|
+
&:after { content: ")"; }
|
55
|
+
}
|
56
|
+
|
57
|
+
.debit .amount,
|
58
|
+
tfoot td {
|
59
|
+
&:before { content: "$"; }
|
60
|
+
}
|
61
|
+
|
62
|
+
tr {
|
63
|
+
&.colour-white {
|
64
|
+
background: $light-white;
|
65
|
+
}
|
66
|
+
&.colour-red {
|
67
|
+
background: $light-red;
|
68
|
+
}
|
69
|
+
&.colour-yellow {
|
70
|
+
background: $light-yellow;
|
71
|
+
}
|
72
|
+
&.colour-green {
|
73
|
+
background: $light-green;
|
74
|
+
}
|
75
|
+
&.colour-blue {
|
76
|
+
background: $light-blue;
|
77
|
+
}
|
78
|
+
&.colour-cyan {
|
79
|
+
background: $light-cyan;
|
80
|
+
}
|
81
|
+
&.colour-magenta {
|
82
|
+
background: $light-magenta;
|
83
|
+
}
|
84
|
+
> td,
|
85
|
+
> th {
|
86
|
+
background: inherit;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
tr.colour-white a.picker,
|
91
|
+
li.colour-white a {
|
92
|
+
background: $white;
|
93
|
+
}
|
94
|
+
|
95
|
+
tr.colour-red a.picker,
|
96
|
+
li.colour-red a {
|
97
|
+
background: $red;
|
98
|
+
}
|
99
|
+
|
100
|
+
tr.colour-green a.picker,
|
101
|
+
li.colour-green a {
|
102
|
+
background: $green;
|
103
|
+
}
|
104
|
+
|
105
|
+
tr.colour-blue a.picker,
|
106
|
+
li.colour-blue a {
|
107
|
+
background: $blue;
|
108
|
+
}
|
109
|
+
|
110
|
+
tr.colour-cyan a.picker,
|
111
|
+
li.colour-cyan a {
|
112
|
+
background: $cyan;
|
113
|
+
}
|
114
|
+
|
115
|
+
tr.colour-magenta a.picker,
|
116
|
+
li.colour-magenta a {
|
117
|
+
background: $magenta;
|
118
|
+
}
|
119
|
+
|
120
|
+
tr.colour-yellow a.picker,
|
121
|
+
li.colour-yellow a {
|
122
|
+
background: $yellow;
|
123
|
+
}
|
124
|
+
|
125
|
+
td.colour {
|
126
|
+
width: 20px;
|
127
|
+
position: relative;
|
128
|
+
|
129
|
+
a {
|
130
|
+
display: block;
|
131
|
+
border: 1px solid #eee;
|
132
|
+
border-radius: 10px;
|
133
|
+
width: 20px;
|
134
|
+
height: 20px;
|
135
|
+
|
136
|
+
&:hover {
|
137
|
+
border-color: #ddd;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
ul {
|
142
|
+
list-style: none;
|
143
|
+
position: absolute;
|
144
|
+
display: block;
|
145
|
+
right: 100%;
|
146
|
+
top: 1px;
|
147
|
+
margin-bottom: 0;
|
148
|
+
width: 167px;
|
149
|
+
text-align: right;
|
150
|
+
background: white;
|
151
|
+
border: 1px solid #eee;
|
152
|
+
border-radius: 50px;
|
153
|
+
padding: 3px;
|
154
|
+
height: 28px;
|
155
|
+
|
156
|
+
li {
|
157
|
+
display: inline-block;
|
158
|
+
margin-right: 3px;
|
159
|
+
|
160
|
+
&:last-child {
|
161
|
+
margin-right: 0;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
}
|
168
|
+
|
169
|
+
footer {
|
170
|
+
font-size: .8em;
|
171
|
+
text-align: center;
|
172
|
+
padding: 20px 0;
|
173
|
+
}
|
data/lib/html/index.html
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<title>Statements</title>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.css"/>
|
7
|
+
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap-theme.css"/>
|
8
|
+
<link rel="stylesheet" href="vendor/datepicker3.css"/>
|
9
|
+
<link rel="stylesheet" href="css/main.css"/>
|
10
|
+
<script src="q/accounts.js"></script>
|
11
|
+
<script src="vendor/jquery-2.1.3.js"></script>
|
12
|
+
<script src="vendor/underscore.js"></script>
|
13
|
+
<script src="vendor/bootstrap/js/bootstrap.js"></script>
|
14
|
+
<script src="vendor/bootstrap-datepicker.js"></script>
|
15
|
+
<script src="js/main.js"></script>
|
16
|
+
</head>
|
17
|
+
<body>
|
18
|
+
<div class="container-fluid">
|
19
|
+
<div class="row">
|
20
|
+
<div class="col-sm-3 sidebar">
|
21
|
+
<form>
|
22
|
+
<div class="form-group">
|
23
|
+
<label for="order">Order</label>
|
24
|
+
<select id="order" name="order" class="form-control">
|
25
|
+
<option selected value="posted_at desc, document_line desc">Newest first</option>
|
26
|
+
<option value="posted_at asc, document_line desc">Oldest first</option>
|
27
|
+
<option value="description asc, posted_at asc, document_line asc">Alphabetical</option>
|
28
|
+
<option value="abs(amount) desc, description asc, posted_at asc, document_line asc">High value first</option>
|
29
|
+
<option value="abs(amount) asc, description asc, posted_at asc, document_line asc">Low value first</option>
|
30
|
+
</select>
|
31
|
+
</div>
|
32
|
+
<div class="form-group">
|
33
|
+
<label for="date-start">Period</label>
|
34
|
+
<div class="input-daterange input-group" id="date-range">
|
35
|
+
<input type="text" class="input-sm form-control" name="date-start" id="date-start" placeholder="Start"/>
|
36
|
+
<span class="input-group-addon">to</span>
|
37
|
+
<input type="text" class="input-sm form-control" name="date-end" id="date-end" placeholder="Finish"/>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
<div class="form-group">
|
41
|
+
<label for="text">Search</label>
|
42
|
+
<input type="text"
|
43
|
+
class="form-control"
|
44
|
+
name="text"
|
45
|
+
id="text"
|
46
|
+
placeholder="Partial description"/>
|
47
|
+
</div>
|
48
|
+
<div class="form-group">
|
49
|
+
<label>Type</label>
|
50
|
+
<div id="type" class="btn-group btn-group-justified" role="group">
|
51
|
+
<a role="button" class="btn btn-default" data-type="credits">Credits</a>
|
52
|
+
<a role="button" class="btn btn-default active" data-type="both">Both</a>
|
53
|
+
<a role="button" class="btn btn-default" data-type="debits">Debits</a>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
<div class="form-group">
|
57
|
+
<label>Colour</label>
|
58
|
+
<div id="colour" class="btn-group btn-group-justified" role="group" data-toggle="buttons">
|
59
|
+
<a role="button" class="btn btn-default active colour-white"></a>
|
60
|
+
<a role="button" class="btn btn-default active colour-red"></a>
|
61
|
+
<a role="button" class="btn btn-default active colour-yellow"></a>
|
62
|
+
<a role="button" class="btn btn-default active colour-green"></a>
|
63
|
+
<a role="button" class="btn btn-default active colour-cyan"></a>
|
64
|
+
<a role="button" class="btn btn-default active colour-blue"></a>
|
65
|
+
<a role="button" class="btn btn-default active colour-magenta"></a>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
<div class="form-group">
|
69
|
+
<label>Accounts</label>
|
70
|
+
<div id="account-buttons"
|
71
|
+
class="btn-group-vertical btn-group-xs"
|
72
|
+
role="group"
|
73
|
+
style="width:100%"
|
74
|
+
data-toggle="buttons"></div>
|
75
|
+
<script>populate_accounts('#account-buttons')</script>
|
76
|
+
</div>
|
77
|
+
<div>
|
78
|
+
<div id="account-button-bulk" class="btn-group btn-group-justified" role="group">
|
79
|
+
<a role="button" class="btn btn-default" data-set="on">All</a>
|
80
|
+
<a role="button" class="btn btn-default" data-set="off">None</a>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
</form>
|
84
|
+
<footer>
|
85
|
+
<p>For my Anna ❤️</p>
|
86
|
+
</footer>
|
87
|
+
</div>
|
88
|
+
<div id="content" class="col-sm-9 content">
|
89
|
+
|
90
|
+
</div>
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
</body>
|
94
|
+
</html>
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# List of colour literals
|
2
|
+
COLOURS = 'white red yellow green cyan blue magenta'.split ' '
|
3
|
+
|
4
|
+
# Populates the list of bank accounts in the sidebar
|
5
|
+
window.populate_accounts = (id) ->
|
6
|
+
$el = $(id)
|
7
|
+
for account in accounts
|
8
|
+
a = $('<a role="button" class="btn btn-default active" />')
|
9
|
+
a.text account.name
|
10
|
+
a.append '<br/>'
|
11
|
+
a.append $('<span class="small">').text(account.number)
|
12
|
+
a.data id: account.id
|
13
|
+
$el.append a
|
14
|
+
|
15
|
+
# AJAX layer
|
16
|
+
post = (page, data) -> $.ajax
|
17
|
+
url: "q/#{page}"
|
18
|
+
method: 'post'
|
19
|
+
contentType: 'application/json; charset=UTF-8'
|
20
|
+
dataType: page.match(/[^.]+$/)[0]
|
21
|
+
data: JSON.stringify data
|
22
|
+
|
23
|
+
# Method to query the database
|
24
|
+
querying = false
|
25
|
+
query_again = false
|
26
|
+
query = ->
|
27
|
+
|
28
|
+
# Handle interrupts
|
29
|
+
if querying
|
30
|
+
query_again = true
|
31
|
+
return
|
32
|
+
querying = true
|
33
|
+
query_again = false
|
34
|
+
|
35
|
+
# Clear content
|
36
|
+
$('#content').html 'One moment...'
|
37
|
+
|
38
|
+
# Submit data
|
39
|
+
req = post 'search.html',
|
40
|
+
order: $('#order').val()
|
41
|
+
date_start: $('#date-start').val()
|
42
|
+
date_end: $('#date-end').val()
|
43
|
+
search: $('#text').val()
|
44
|
+
type: $('#type .active').data 'type'
|
45
|
+
accounts: $('#account-buttons .active').map(-> $(@).data 'id').toArray()
|
46
|
+
colours: $('#colour .active').map(-> @className.match(/colour-(\w+)/)[1]).toArray()
|
47
|
+
|
48
|
+
# On success
|
49
|
+
req.done (result) -> $('#content').html result
|
50
|
+
|
51
|
+
# On fail
|
52
|
+
req.fail -> alert 'Query failed'
|
53
|
+
|
54
|
+
# Clean up
|
55
|
+
req.always ->
|
56
|
+
querying = false
|
57
|
+
query() if query_again
|
58
|
+
|
59
|
+
# Pad querying out a bit
|
60
|
+
query = _.debounce(query, 300)
|
61
|
+
|
62
|
+
# Method to change a transaction's colour
|
63
|
+
setColour = (transactionId, colour) -> post 'colour.json',
|
64
|
+
id: transactionId
|
65
|
+
colour: colour
|
66
|
+
|
67
|
+
$ ->
|
68
|
+
# Set the date range to last financial year
|
69
|
+
months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split ' '
|
70
|
+
range = end: new Date
|
71
|
+
range.end.setDate 1
|
72
|
+
range.end.setMonth range.end.getMonth() - 1 until range.end.getMonth() is 6
|
73
|
+
range.start = new Date(range.end)
|
74
|
+
range.start.setFullYear range.start.getFullYear() - 1
|
75
|
+
range.end.setDate range.end.getDate() - 1
|
76
|
+
|
77
|
+
$("#date-#{k}").val "#{i.getDate()} #{months[i.getMonth()]} #{i.getFullYear()}" for k, i of range
|
78
|
+
|
79
|
+
# Set up the date picker
|
80
|
+
$('.input-daterange').datepicker
|
81
|
+
format: "d M yyyy"
|
82
|
+
startView: 2
|
83
|
+
todayBtn: 'linked'
|
84
|
+
autoclose: true
|
85
|
+
.on 'changeDate', query
|
86
|
+
|
87
|
+
# Order
|
88
|
+
$('#order').on 'change', query
|
89
|
+
|
90
|
+
# Dates
|
91
|
+
$('date-range').on 'change', 'input', query
|
92
|
+
|
93
|
+
# Search
|
94
|
+
$('#text').on 'change keyup', query
|
95
|
+
|
96
|
+
# Colours
|
97
|
+
$('#colour').on 'click', 'a', query
|
98
|
+
|
99
|
+
# Account buttons
|
100
|
+
$accountButtons = $('#account-buttons a')
|
101
|
+
$accountButtons.on 'click', query
|
102
|
+
|
103
|
+
# Account select all/none buttons
|
104
|
+
$('#account-button-bulk').on 'click', 'a', (e) ->
|
105
|
+
set = $(e.currentTarget).data('set')
|
106
|
+
$accountButtons.toggleClass 'active', set is 'on'
|
107
|
+
query()
|
108
|
+
|
109
|
+
# Radio button behaviour for transaction types
|
110
|
+
$typeButtons = $('#type a')
|
111
|
+
$('#type').on 'click', 'a', (e) ->
|
112
|
+
return if $(e.currentTarget).hasClass 'active'
|
113
|
+
$typeButtons.each -> $(this).toggleClass 'active', this is e.currentTarget
|
114
|
+
query()
|
115
|
+
|
116
|
+
# Handler for blurring colour pickers
|
117
|
+
$picker = null
|
118
|
+
$('body').on 'click', (e) ->
|
119
|
+
target = e.currentTarget
|
120
|
+
closePicker() unless $picker and (target is $picker[0] or $picker.find(target)[0])
|
121
|
+
|
122
|
+
# Method to remove colour picker
|
123
|
+
closePicker = ->
|
124
|
+
$picker.remove() if $picker
|
125
|
+
$picker = null
|
126
|
+
|
127
|
+
# Handlers for clicks on colour pickers
|
128
|
+
$('#content').on 'click', 'td.colour a.picker', (e) ->
|
129
|
+
e.stopPropagation()
|
130
|
+
closePicker()
|
131
|
+
$tr = $(e.currentTarget).parents('tr').first()
|
132
|
+
$td = $tr.find('td.colour')
|
133
|
+
$picker = $('<ul class="colours">')
|
134
|
+
$picker.append $('<li>').addClass("colour-#{i}").append('<a href="javascript:">') for i in COLOURS
|
135
|
+
$picker.appendTo $td
|
136
|
+
$picker.on 'click', 'a', (e) ->
|
137
|
+
$li = $(e.currentTarget).parents('li').first()
|
138
|
+
colour = $li[0].className.match(/colour-(\w+)/)[1]
|
139
|
+
op = setColour $tr.data('id'), colour
|
140
|
+
op.fail -> alert 'Something went wrong. Try reloading?'
|
141
|
+
op.done -> $tr[0].className = $tr[0].className.replace /colour-\w+/, "colour-#{colour}"
|
142
|
+
closePicker()
|
143
|
+
|
144
|
+
|
145
|
+
# Do an initial query
|
146
|
+
query()
|