blob: 0ed8245c300252dc399b9861fd1196727fcdcd7b [file] [log] [blame]
Sergiusz Bazanskifb18c992019-05-01 12:27:03 +02001package main
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "strconv"
8
9 "github.com/golang/glog"
10 "github.com/golang/protobuf/proto"
11 _ "github.com/mattn/go-sqlite3"
12 "google.golang.org/grpc/codes"
13 "google.golang.org/grpc/status"
14
15 pb "code.hackerspace.pl/hscloud/proto/invoice"
16)
17
18type model struct {
19 db *sql.DB
20}
21
22func newModel(dsn string) (*model, error) {
23 db, err := sql.Open("sqlite3", dsn)
24 if err != nil {
25 return nil, err
26 }
27 return &model{
28 db: db,
29 }, nil
30}
31
32func (m *model) init() error {
33 _, err := m.db.Exec(`
34 create table invoice (
35 id integer primary key not null,
36 proto blob not null
37 );
38 create table invoice_seal (
39 id integer primary key not null,
40 invoice_id integer not null,
41 final_uid text not null unique,
42 foreign key (invoice_id) references invoice(id)
43 );
44 create table invoice_blob (
45 id integer primary key not null,
46 invoice_id integer not null,
47 pdf blob not null,
48 foreign key (invoice_id) references invoice(id)
49 );
50 `)
51 return err
52}
53
54func (m *model) sealInvoice(ctx context.Context, uid string) error {
55 id, err := strconv.Atoi(uid)
56 if err != nil {
57 return status.Error(codes.InvalidArgument, "invalid uid")
58 }
59
60 invoice, err := m.getInvoice(ctx, uid)
61 if err != nil {
62 return err
63 }
64
65 tx, err := m.db.BeginTx(ctx, nil)
66 if err != nil {
67 return err
68 }
69
70 q := `
71 insert into invoice_seal (
72 invoice_id, final_uid
73 ) values (
74 ?,
75 ( select printf("%04d", ifnull( (select final_uid as v from invoice_seal order by final_uid desc limit 1), 19000) + 1 ))
76 )
77
78 `
79 res, err := tx.Exec(q, id)
80 if err != nil {
81 return err
82 }
83
84 lastInvoiceSealId, err := res.LastInsertId()
85 if err != nil {
86 return err
87 }
88
89 q = `
90 select final_uid from invoice_seal where id = ?
91 `
92
93 var finalUid string
94 if err := tx.QueryRow(q, lastInvoiceSealId).Scan(&finalUid); err != nil {
95 return err
96 }
97
98 q = `
99 insert into invoice_blob (
100 invoice_id, pdf
101 ) values (
102 ?,
103 ?
104 )
105 `
106
107 pdfBlob, err := renderInvoicePDF(invoice, finalUid, false)
108 if err != nil {
109 return err
110 }
111
112 if _, err := tx.Exec(q, id, pdfBlob); err != nil {
113 return err
114 }
115
116 if err := tx.Commit(); err != nil {
117 return err
118 }
119
120 return nil
121}
122
123func (m *model) createInvoice(ctx context.Context, i *pb.Invoice) (string, error) {
124 data, err := proto.Marshal(i)
125 if err != nil {
126 return "", err
127 }
128
129 sql := `
130 insert into invoice (
131 proto
132 ) values (
133 ?
134 )
135 `
136 res, err := m.db.Exec(sql, data)
137 if err != nil {
138 return "", err
139 }
140 id, err := res.LastInsertId()
141 if err != nil {
142 return "", err
143 }
144
145 glog.Infof("%+v", id)
146 return fmt.Sprintf("%d", id), nil
147}
148
149func (m *model) getRendered(ctx context.Context, uid string) ([]byte, error) {
150 id, err := strconv.Atoi(uid)
151 if err != nil {
152 return nil, status.Error(codes.InvalidArgument, "invalid uid")
153 }
154
155 q := `
156 select invoice_blob.pdf from invoice_blob where invoice_blob.invoice_id = ?
157 `
158 res := m.db.QueryRow(q, id)
159
160 data := []byte{}
161 if err := res.Scan(&data); err != nil {
162 if err == sql.ErrNoRows {
163 return nil, status.Error(codes.InvalidArgument, "no such invoice")
164 }
165 return nil, err
166 }
167 return data, nil
168}
169
170func (m *model) getSealedUid(ctx context.Context, uid string) (string, error) {
171 id, err := strconv.Atoi(uid)
172 if err != nil {
173 return "", status.Error(codes.InvalidArgument, "invalid uid")
174 }
175
176 q := `
177 select invoice_seal.final_uid from invoice_seal where invoice_seal.invoice_id = ?
178 `
179 res := m.db.QueryRow(q, id)
180 finalUid := ""
181 if err := res.Scan(&finalUid); err != nil {
182 if err == sql.ErrNoRows {
183 return "", nil
184 }
185 return "", err
186 }
187 return finalUid, nil
188}
189
190func (m *model) getInvoice(ctx context.Context, uid string) (*pb.Invoice, error) {
191 id, err := strconv.Atoi(uid)
192 if err != nil {
193 return nil, status.Error(codes.InvalidArgument, "invalid uid")
194 }
195
196 q := `
197 select invoice.proto from invoice where invoice.id = ?
198 `
199 res := m.db.QueryRow(q, id)
200 data := []byte{}
201 if err := res.Scan(&data); err != nil {
202 if err == sql.ErrNoRows {
203 return nil, status.Error(codes.NotFound, "no such invoice")
204 }
205 return nil, err
206 }
207
208 p := &pb.Invoice{}
209 if err := proto.Unmarshal(data, p); err != nil {
210 return nil, err
211 }
212 return p, nil
213}