/
models.go
228 lines (193 loc) · 7.19 KB
/
models.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package tabscanner
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"net/textproto"
)
// StatusCode represents the status of an operation.
type StatusCode int
// Known status codes.
const (
StatusCodePending StatusCode = 1
StatusCodeSuccess StatusCode = 2
StatusCodeDone StatusCode = 3
StatusCodeFailed StatusCode = 4
)
// Code represents an operation result code.
type Code int
// Known codes.
const (
CodeImageUploadedSuccessfully Code = 200
CodeAPIKeyAuthenticated Code = 201
CodeResultAvailable Code = 202
CodeImageUploadedButDidNotMeetTheRecommendedDimension Code = 300
CodeResultNotYetAvailable Code = 301
CodeAPIKeyNotFound Code = 400
CodeNotEnoughCredit Code = 401
CodeTokenNotFound Code = 402
CodeNoFileDetected Code = 403
CodeMultipleFilesDetected Code = 404
CodeUnsupportedMimeType Code = 405
CodeFormParserError Code = 406
CodeUnsupportedFileExtension Code = 407
CodeFileSystemError Code = 408
CodeOCRFailure Code = 500
CodeServerError Code = 510
CodeDatabaseConnectionError Code = 520
CodeDatabaseQueryError Code = 521
)
// Language describes the language of a receipt.
type Language string
// Known languages.
const (
LanguageEnglish Language = "english"
LanguageSpanish Language = "spanish"
)
// DocumentType describes a document type.
type DocumentType string
// Known document types.
const (
DocumentTypeReceipt DocumentType = "receipt"
DocumentTypeInvoice DocumentType = "invoice"
DocumentTypeAuto DocumentType = "auto"
)
// IntPtr returns a pointer to the given int.
func IntPtr(i int) *int {
return &i
}
// BoolPtr returns a pointer to the given bool.
func BoolPtr(b bool) *bool {
return &b
}
// ResponseHeader describes the common fields in responses.
type ResponseHeader struct {
Message string `json:"message"`
Status string `json:"status"`
StatusCode StatusCode `json:"status_code"`
Token string `json:"token"`
Success bool `json:"success"`
Code Code `json:"code"`
}
// ProcessRequest describes a process request. Specify only one of ReceiptImageHeader or ReceiptImage data.
type ProcessRequest struct {
ReceiptImage []byte // mime-type is automatically detected
DecimalPlaces *int // valid values: nil, 0, 2, 3
Language Language // valid values: "", LanguageEnglish, LanguageSpanish
Cents *bool // valid values: nil, false, true
LineExtract *bool // valid values: nil, false, true
DocumentType DocumentType // valid values: "", DocumentTypeReceipt, DocumentTypeInvoice, DocumentTypeAuto
TestMode *bool // valid values: nil, false, true
}
func (r *ProcessRequest) toFormBody() (io.Reader, string, error) {
body := &bytes.Buffer{}
w := multipart.NewWriter(body)
if err := r.writeReceipt(w); err != nil {
return nil, "", err
}
if r.DecimalPlaces != nil {
if err := w.WriteField("decimalPlaces", fmt.Sprintf("%v", *r.DecimalPlaces)); err != nil {
return nil, "", err
}
}
if r.Language != "" {
if err := w.WriteField("language", string(r.Language)); err != nil {
return nil, "", err
}
}
if r.Cents != nil {
if err := w.WriteField("cents", fmt.Sprintf("%v", *r.Cents)); err != nil {
return nil, "", err
}
}
if r.LineExtract != nil {
if err := w.WriteField("lineExtract", fmt.Sprintf("%v", *r.LineExtract)); err != nil {
return nil, "", err
}
}
if r.DocumentType != "" {
if err := w.WriteField("documentType", string(r.DocumentType)); err != nil {
return nil, "", err
}
}
if r.TestMode != nil {
if err := w.WriteField("testMode", fmt.Sprintf("%v", *r.TestMode)); err != nil {
return nil, "", err
}
}
if err := w.Close(); err != nil {
return nil, "", err
}
return body, w.FormDataContentType(), nil
}
func (r *ProcessRequest) writeReceipt(w *multipart.Writer) error {
receiptMimeType, receiptExt, err := r.getType()
if err != nil {
return err
}
receiptHeader := textproto.MIMEHeader{}
receiptHeader.Set("Content-Disposition", fmt.Sprintf(`form-data; name="receiptImage"; filename="receipt.%v"`, receiptExt))
receiptHeader.Set("Content-Type", receiptMimeType)
receiptPart, err := w.CreatePart(receiptHeader)
if err != nil {
return err
}
_, err = io.Copy(receiptPart, bytes.NewReader(r.ReceiptImage))
return err
}
func (r *ProcessRequest) getType() (string, string, error) {
mimeType := http.DetectContentType(r.ReceiptImage)
extensions, err := mime.ExtensionsByType(mimeType)
if err != nil {
return "", "", err
}
if len(extensions) == 0 {
return mimeType, "bin", nil
}
return mimeType, extensions[0], nil
}
// ProcessResponse describes a process response.
type ProcessResponse struct {
*ResponseHeader
Duplicate bool `json:"duplicate"`
DuplicateToken string `json:"duplicateToken"`
}
// ResultResponse describes a result response.
type ResultResponse struct {
*ResponseHeader
Result *ResultResponseResult `json:"result"`
}
// ResultResponseResult describes the result field in a result response.
type ResultResponseResult struct {
Establishment string `json:"establishment"`
ValidatedEstablishment bool `json:"validatedEstablishment"`
Date string `json:"date"`
Total string `json:"total"`
URL string `json:"url"`
PhoneNumber string `json:"phoneNumber"`
PaymentMethod string `json:"paymentMethod"`
Address string `json:"address"`
ValidatedTotal bool `json:"validatedTotal"`
SubTotal string `json:"subTotal"`
ValidatedSubTotal bool `json:"validatedSubTotal"`
Cash string `json:"cash"`
Change string `json:"change"`
Tax string `json:"tax"`
Taxes []json.Number `json:"taxes"`
Discount string `json:"discount"`
Discounts []json.Number `json:"discounts"`
LineItems []*ResultResponseResultLineItem `json:"lineItems"`
}
// ResultResponseResultLineItem describes a line item in a result.
type ResultResponseResultLineItem struct {
Quantity json.Number `json:"qty"`
Description string `json:"desc"`
Unit string `json:"unit"`
CleanDescription string `json:"descClean"`
LineTotal string `json:"lineTotal"`
ProductCode string `json:"productCode"`
}