mindee 1.0.6 → 1.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.
Files changed (55) hide show
  1. package/.nyc_output/f57ba3bc-1777-4ecc-81e1-230327c2a401.json +1 -0
  2. package/.nyc_output/processinfo/f57ba3bc-1777-4ecc-81e1-230327c2a401.json +1 -0
  3. package/.nyc_output/processinfo/index.json +1 -0
  4. package/CHANGELOG.md +37 -2
  5. package/LICENSE +202 -0
  6. package/README.md +1 -1
  7. package/coverage/coverage.json +1 -0
  8. package/coverage/lcov-report/base.css +224 -0
  9. package/coverage/lcov-report/block-navigation.js +79 -0
  10. package/coverage/lcov-report/favicon.png +0 -0
  11. package/coverage/lcov-report/index.html +171 -0
  12. package/coverage/lcov-report/prettify.css +1 -0
  13. package/coverage/lcov-report/prettify.js +2 -0
  14. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  15. package/coverage/lcov-report/sorter.js +170 -0
  16. package/coverage/lcov.info +1595 -0
  17. package/coverage.lcov +1595 -0
  18. package/lib/api/financialDocument.js +5 -5
  19. package/lib/api/invoice.js +3 -3
  20. package/lib/api/object.js +2 -2
  21. package/lib/api/receipt.js +3 -3
  22. package/lib/api/request.js +12 -6
  23. package/lib/api/response.js +8 -3
  24. package/lib/documents/document.js +5 -1
  25. package/lib/documents/fields/field.js +1 -1
  26. package/lib/documents/financialDocument.js +36 -28
  27. package/lib/documents/invoice.js +49 -31
  28. package/lib/documents/receipt.js +40 -25
  29. package/lib/inputs.js +78 -24
  30. package/mindee/api/financialDocument.js +47 -0
  31. package/mindee/api/index.js +5 -0
  32. package/mindee/api/invoice.js +53 -0
  33. package/mindee/api/object.js +82 -0
  34. package/mindee/api/receipt.js +39 -0
  35. package/mindee/api/request.js +70 -0
  36. package/mindee/api/response.js +65 -0
  37. package/mindee/documents/document.js +69 -0
  38. package/mindee/documents/fields/amount.js +25 -0
  39. package/mindee/documents/fields/date.js +29 -0
  40. package/mindee/documents/fields/field.js +89 -0
  41. package/mindee/documents/fields/index.js +7 -0
  42. package/mindee/documents/fields/locale.js +30 -0
  43. package/mindee/documents/fields/orientation.js +24 -0
  44. package/mindee/documents/fields/paymentDetails.js +52 -0
  45. package/mindee/documents/fields/tax.js +47 -0
  46. package/mindee/documents/financialDocument.js +228 -0
  47. package/mindee/documents/index.js +5 -0
  48. package/mindee/documents/invoice.js +411 -0
  49. package/mindee/documents/receipt.js +287 -0
  50. package/mindee/errors/handler.js +16 -0
  51. package/mindee/index.js +36 -0
  52. package/mindee/inputs.js +156 -0
  53. package/mindee/logger.js +33 -0
  54. package/package.json +7 -3
  55. package/LICENSE.md +0 -674
@@ -0,0 +1,411 @@
1
+ const Document = require("./document");
2
+ const Field = require("./fields").field;
3
+ const Date = require("./fields").date;
4
+ const Amount = require("./fields").amount;
5
+ const Locale = require("./fields").locale;
6
+ const Orientation = require("./fields").orientation;
7
+ const PaymentDetails = require("./fields").paymentDetails;
8
+ const Tax = require("./fields").tax;
9
+
10
+ class Invoice extends Document {
11
+ /**
12
+ * @param {Object} apiPrediction - Json parsed prediction from HTTP response
13
+ * @param {Input} input - Input object
14
+ * @param {Object} locale - locale value for creating Invoice object from scratch
15
+ * @param {Object} totalIncl - total tax included value for creating Invoice object from scratch
16
+ * @param {Object} totalExcl - total tax excluded value for creating Invoice object from scratch
17
+ * @param {Object} invoiceDate - invoice date value for creating Invoice object from scratch
18
+ * @param {Object} invoiceNumber - invoice number value for creating Invoice object from scratch
19
+ * @param {Object} taxes - taxes value for creating Invoice object from scratch
20
+ * @param {Object} supplier - supplier value for creating Invoice object from scratch
21
+ * @param {Object} paymentDetails - payment details value for creating Invoice object from scratch
22
+ * @param {Object} companyNumber - company number value for creating Invoice object from scratch
23
+ * @param {Object} vatNumber - vat number value for creating Invoice object from scratch
24
+ * @param {Object} orientation - orientation value for creating Invoice object from scratch
25
+ * @param {Object} totalTax - total tax value for creating Invoice object from scratch
26
+ * @param {Number} pageNumber - pageNumber for multi pages pdf input
27
+ * @param {String} level - specify whether object is built from "page" level or "document" level prediction
28
+ */
29
+ constructor({
30
+ apiPrediction = undefined,
31
+ inputFile = undefined,
32
+ locale = undefined,
33
+ totalIncl = undefined,
34
+ totalExcl = undefined,
35
+ invoiceDate = undefined,
36
+ invoiceNumber = undefined,
37
+ dueDate = undefined,
38
+ taxes = undefined,
39
+ supplier = undefined,
40
+ paymentDetails = undefined,
41
+ companyNumber = undefined,
42
+ vatNumber = undefined,
43
+ orientation = undefined,
44
+ totalTax = undefined,
45
+ pageNumber = 0,
46
+ level = "page",
47
+ }) {
48
+ super(inputFile);
49
+ this.level = level;
50
+ this.constructPrediction = function (item) {
51
+ return { prediction: { value: item }, valueKey: "value", pageNumber };
52
+ };
53
+ if (apiPrediction === undefined) {
54
+ this.#initFromScratch({
55
+ locale,
56
+ totalIncl,
57
+ totalExcl,
58
+ invoiceDate,
59
+ invoiceNumber,
60
+ dueDate,
61
+ taxes,
62
+ supplier,
63
+ paymentDetails,
64
+ companyNumber,
65
+ vatNumber,
66
+ orientation,
67
+ pageNumber,
68
+ totalTax,
69
+ });
70
+ } else {
71
+ this.#initFromApiPrediction(apiPrediction, pageNumber);
72
+ }
73
+ this.#checklist();
74
+ this.#reconstruct();
75
+ }
76
+
77
+ #initFromScratch({
78
+ locale,
79
+ totalIncl,
80
+ totalExcl,
81
+ totalTax,
82
+ invoiceDate,
83
+ invoiceNumber,
84
+ dueDate,
85
+ taxes,
86
+ supplier,
87
+ paymentDetails,
88
+ companyNumber,
89
+ vatNumber,
90
+ orientation,
91
+ pageNumber,
92
+ }) {
93
+ this.locale = new Locale(this.constructPrediction(locale));
94
+ this.totalIncl = new Amount(this.constructPrediction(totalIncl));
95
+ this.totalExcl = new Amount(this.constructPrediction(totalExcl));
96
+ this.totalTax = new Amount(this.constructPrediction(totalTax));
97
+ this.date = new Date(this.constructPrediction(invoiceDate));
98
+ this.invoiceDate = new Date(this.constructPrediction(invoiceDate));
99
+ this.dueDate = new Date(this.constructPrediction(dueDate));
100
+ this.supplier = new Field(this.constructPrediction(supplier));
101
+ this.invoiceNumber = new Field(this.constructPrediction(invoiceNumber));
102
+ this.paymentDetails = new Field(this.constructPrediction(paymentDetails));
103
+ this.companyNumber = new Field(this.constructPrediction(companyNumber));
104
+ this.vatNumber = new Field(this.constructPrediction(vatNumber));
105
+ if (taxes !== undefined) {
106
+ this.taxes = [];
107
+ for (const t of taxes) {
108
+ this.taxes.push(
109
+ new Tax({
110
+ prediction: { value: t[0], rate: t[1] },
111
+ pageNumber,
112
+ valueKey: "value",
113
+ rateKey: "rate",
114
+ })
115
+ );
116
+ }
117
+ }
118
+ if (this.level === "page") {
119
+ this.orientation = new Orientation(this.constructPrediction(orientation));
120
+ }
121
+ }
122
+
123
+ #initFromApiPrediction(apiPrediction, pageNumber) {
124
+ this.words = [];
125
+ this.locale = new Locale({ prediction: apiPrediction.locale, pageNumber });
126
+ this.totalIncl = new Amount({
127
+ prediction: apiPrediction.total_incl,
128
+ valueKey: "value",
129
+ pageNumber,
130
+ });
131
+ this.totalTax = new Amount({
132
+ prediction: { value: undefined, probability: 0.0 },
133
+ valueKey: "value",
134
+ pageNumber,
135
+ });
136
+ this.totalExcl = new Amount({
137
+ prediction: apiPrediction.total_excl,
138
+ valueKey: "value",
139
+ pageNumber,
140
+ });
141
+ this.date = new Date({
142
+ prediction: apiPrediction.date,
143
+ valueKey: "value",
144
+ pageNumber,
145
+ });
146
+ this.invoiceDate = new Date({
147
+ prediction: apiPrediction.date,
148
+ valueKey: "value",
149
+ pageNumber,
150
+ });
151
+ this.taxes = apiPrediction.taxes.map(function (taxPrediction) {
152
+ return new Tax({
153
+ prediction: taxPrediction,
154
+ pageNumber,
155
+ valueKey: "value",
156
+ rateKey: "rate",
157
+ codeKey: "code",
158
+ });
159
+ });
160
+ this.companyNumber = apiPrediction.company_registration.map(function (
161
+ companyNumber
162
+ ) {
163
+ return new Field({
164
+ prediction: companyNumber,
165
+ pageNumber,
166
+ extraFields: ["type"],
167
+ });
168
+ });
169
+ this.dueDate = new Date({
170
+ prediction: apiPrediction.due_date,
171
+ valueKey: "value",
172
+ pageNumber,
173
+ });
174
+ this.invoiceNumber = new Field({
175
+ prediction: apiPrediction.invoice_number,
176
+ pageNumber,
177
+ });
178
+ this.supplier = new Field({
179
+ prediction: apiPrediction.supplier,
180
+ pageNumber,
181
+ });
182
+ this.paymentDetails = apiPrediction.payment_details.map(function (
183
+ paymentDetail
184
+ ) {
185
+ return new PaymentDetails({ prediction: paymentDetail, pageNumber });
186
+ });
187
+ if (this.level === "page") {
188
+ this.orientation = new Orientation({
189
+ prediction: apiPrediction.orientation,
190
+ pageNumber,
191
+ });
192
+ } else {
193
+ this.orientation = new Orientation(
194
+ this.constructPrediction({
195
+ prediction: {
196
+ value: undefined,
197
+ probability: 0.0,
198
+ degrees: undefined,
199
+ },
200
+ })
201
+ );
202
+ }
203
+ // document.inference.ocr
204
+ if ("mvision" in apiPrediction) this.words = apiPrediction.mvision;
205
+ }
206
+
207
+ toString() {
208
+ return `
209
+ -----Invoice data-----
210
+ Filename: ${this.filename}
211
+ Invoice number: ${this.invoiceNumber.value}
212
+ Total amount including taxes: ${this.totalIncl.value}
213
+ Total amount excluding taxes: ${this.totalExcl.value}
214
+ Invoice Date: ${this.invoiceDate.value}
215
+ Supplier name: ${this.supplier.value}
216
+ Taxes: ${this.taxes.map((tax) => tax.toString()).join(" - ")}
217
+ Total taxes: ${this.totalTax.value}
218
+ `;
219
+ }
220
+
221
+ #checklist() {
222
+ this.checklist = {
223
+ taxesMatchTotalIncl: this.#taxesMatchTotalIncl(),
224
+ taxesMatchTotalExcl: this.#taxesMatchTotalExcl(),
225
+ taxesPlusTotalExclMatchTotalIncl: this.#taxesPlusTotalExclMatchTotalIncl(),
226
+ };
227
+ }
228
+
229
+ #reconstruct() {
230
+ this.#reconstructTotalTax();
231
+ this.#reconstructTotalExcl();
232
+ this.#reconstructTotalIncl();
233
+ this.#reconstructTotalTaxFromTotals();
234
+ }
235
+
236
+ #taxesMatchTotalIncl() {
237
+ // Check taxes and total include exist
238
+ if (this.taxes.length === 0 || this.totalIncl.value === undefined)
239
+ return false;
240
+
241
+ // Reconstruct totalIncl from taxes
242
+ let totalVat = 0;
243
+ let reconstructedTotal = 0;
244
+ this.taxes.forEach((tax) => {
245
+ if (tax.value === undefined || !tax.rate) return false;
246
+ totalVat += tax.value;
247
+ reconstructedTotal += tax.value + (100 * tax.value) / tax.rate;
248
+ });
249
+
250
+ // Sanity check
251
+ if (totalVat <= 0) return false;
252
+
253
+ // Crate epsilon
254
+ const eps = 1 / (100 * totalVat);
255
+
256
+ if (
257
+ this.totalIncl.value * (1 - eps) - 0.02 <= reconstructedTotal &&
258
+ reconstructedTotal <= this.totalIncl.value * (1 + eps) + 0.02
259
+ ) {
260
+ this.taxes = this.taxes.map((tax) => ({ ...tax, probability: 1.0 }));
261
+ this.totalTax.probability = 1.0;
262
+ this.totalIncl.probability = 1.0;
263
+ return true;
264
+ }
265
+ return false;
266
+ }
267
+
268
+ /**
269
+ *
270
+ */
271
+ #taxesMatchTotalExcl() {
272
+ // Check taxes and total amount exist
273
+ if (this.taxes.length === 0 || this.totalExcl.value == null) return false;
274
+
275
+ // Reconstruct total_incl from taxes
276
+ let totalVat = 0;
277
+ let reconstructedTotal = 0;
278
+ this.taxes.forEach((tax) => {
279
+ if (tax.value == null || !tax.rate) return false;
280
+ totalVat += tax.value;
281
+ reconstructedTotal += (100 * tax.value) / tax.rate;
282
+ });
283
+
284
+ // Sanity check
285
+ if (totalVat <= 0) return false;
286
+
287
+ // Crate epsilon
288
+ const eps = 1 / (100 * totalVat);
289
+
290
+ if (
291
+ this.totalExcl.value * (1 - eps) - 0.02 <= reconstructedTotal &&
292
+ reconstructedTotal <= this.totalExcl.value * (1 + eps) + 0.02
293
+ ) {
294
+ this.taxes = this.taxes.map((tax) => ({ ...tax, probability: 1.0 }));
295
+ this.totalTax.probability = 1.0;
296
+ this.totalExcl.probability = 1.0;
297
+ return true;
298
+ }
299
+ return false;
300
+ }
301
+
302
+ #taxesPlusTotalExclMatchTotalIncl() {
303
+ if (
304
+ this.totalExcl.value === undefined ||
305
+ this.taxes.length == 0 ||
306
+ this.totalIncl === undefined
307
+ )
308
+ return false;
309
+ let totalVat = 0;
310
+ this.taxes.forEach((tax) => (totalVat += tax.value));
311
+ const reconstructedTotal = totalVat + this.totalExcl.value;
312
+
313
+ if (totalVat <= 0) return false;
314
+
315
+ if (
316
+ this.totalIncl.value - 0.01 <= reconstructedTotal &&
317
+ reconstructedTotal <= this.totalIncl.value + 0.01
318
+ ) {
319
+ this.taxes = this.taxes.map((tax) => ({ ...tax, probability: 1.0 }));
320
+ this.totalTax.probability = 1.0;
321
+ this.totalIncl.probability = 1.0;
322
+ return true;
323
+ }
324
+ return false;
325
+ }
326
+
327
+ #reconstructTotalTax() {
328
+ if (this.taxes.length > 0) {
329
+ const totalTax = {
330
+ value: this.taxes.reduce((acc, tax) => {
331
+ return tax.value !== undefined ? acc + tax.value : acc;
332
+ }, 0),
333
+ probability: Field.arrayProbability(this.taxes),
334
+ };
335
+ if (totalTax.value > 0)
336
+ this.totalTax = new Amount({
337
+ prediction: totalTax,
338
+ valueKey: "value",
339
+ reconstructed: true,
340
+ });
341
+ }
342
+ }
343
+
344
+ #reconstructTotalTaxFromTotals() {
345
+ if (
346
+ this.totalTax.value === undefined &&
347
+ this.totalIncl.value > 0 &&
348
+ this.totalExcl.value > 0 &&
349
+ this.totalExcl.value <= this.totalIncl.value
350
+ ) {
351
+ const totalTax = {
352
+ value: this.totalIncl.value - this.totalExcl.value,
353
+ probability: this.totalIncl.probability * this.totalExcl.probability,
354
+ };
355
+ if (totalTax.value > 0)
356
+ this.totalTax = new Amount({
357
+ prediction: totalTax,
358
+ valueKey: "value",
359
+ reconstructed: true,
360
+ });
361
+ }
362
+ }
363
+
364
+ #reconstructTotalExcl() {
365
+ if (
366
+ this.taxes.length &&
367
+ this.totalIncl.value != null &&
368
+ this.totalExcl.value === undefined
369
+ ) {
370
+ const totalExcl = {
371
+ value:
372
+ this.totalIncl.value -
373
+ this.taxes.reduce((acc, tax) => {
374
+ return tax.value !== undefined ? acc + tax.value : acc;
375
+ }, 0),
376
+ probability:
377
+ Field.arrayProbability(this.taxes) * this.totalIncl.probability,
378
+ };
379
+ this.totalExcl = new Amount({
380
+ prediction: totalExcl,
381
+ valueKey: "value",
382
+ reconstructed: true,
383
+ });
384
+ }
385
+ }
386
+
387
+ #reconstructTotalIncl() {
388
+ if (
389
+ this.taxes.length &&
390
+ this.totalExcl.value != null &&
391
+ this.totalIncl.value === undefined
392
+ ) {
393
+ const totalIncl = {
394
+ value:
395
+ this.totalExcl.value +
396
+ this.taxes.reduce((acc, tax) => {
397
+ return tax.value ? acc + tax.value : acc;
398
+ }, 0.0),
399
+ probability:
400
+ Field.arrayProbability(this.taxes) * this.totalExcl.probability,
401
+ };
402
+ this.totalIncl = new Amount({
403
+ prediction: totalIncl,
404
+ valueKey: "value",
405
+ reconstructed: true,
406
+ });
407
+ }
408
+ }
409
+ }
410
+
411
+ module.exports = Invoice;
@@ -0,0 +1,287 @@
1
+ const Document = require("./document");
2
+ const Field = require("./fields").field;
3
+ const Date = require("./fields").date;
4
+ const Amount = require("./fields").amount;
5
+ const Locale = require("./fields").locale;
6
+ const Orientation = require("./fields").orientation;
7
+ const Tax = require("./fields").tax;
8
+ const fs = require("fs").promises;
9
+
10
+ class Receipt extends Document {
11
+ /**
12
+ * @param {Object} apiPrediction - Json parsed prediction from HTTP response
13
+ * @param {Input} input - Input object
14
+ * @param {Integer} pageNumber - Page number for multi pages pdf input
15
+ * @param {Object} locale - locale value for creating Receipt object from scratch
16
+ * @param {Object} totalIncl - total tax Included value for creating Receipt object from scratch
17
+ * @param {Object} date - date value for creating Receipt object from scratch
18
+ * @param {Object} category - category value for creating Receipt object from scratch
19
+ * @param {Object} merchantName - merchant name value for creating Receipt object from scratch
20
+ * @param {Object} time - time value for creating Receipt object from scratch
21
+ * @param {Object} taxes - taxes value for creating Receipt object from scratch
22
+ * @param {Object} orientation - orientation value for creating Receipt object from scratch
23
+ * @param {Object} totalTax - total taxes value for creating Receipt object from scratch
24
+ * @param {Object} totalExcl - total taxes excluded value for creating Receipt object from scratch
25
+ * @param {String} level - specify whether object is built from "page" level or "document" level prediction
26
+ */
27
+ constructor({
28
+ apiPrediction = undefined,
29
+ inputFile = undefined,
30
+ locale = undefined,
31
+ totalIncl = undefined,
32
+ date = undefined,
33
+ category = undefined,
34
+ merchantName = undefined,
35
+ time = undefined,
36
+ taxes = undefined,
37
+ orientation = undefined,
38
+ totalTax = undefined,
39
+ totalExcl = undefined,
40
+ pageNumber = 0,
41
+ level = "page",
42
+ }) {
43
+ super(inputFile);
44
+ this.level = level;
45
+ this.constructPrediction = function (item) {
46
+ return { prediction: { value: item }, valueKey: "value", pageNumber };
47
+ };
48
+ if (apiPrediction === undefined) {
49
+ this.#initFromScratch({
50
+ locale,
51
+ totalExcl,
52
+ totalIncl,
53
+ date,
54
+ category,
55
+ merchantName,
56
+ time,
57
+ taxes,
58
+ orientation,
59
+ totalTax,
60
+ pageNumber,
61
+ });
62
+ } else {
63
+ this.#initFromApiPrediction(apiPrediction, pageNumber);
64
+ }
65
+ this.#checklist();
66
+ this.#reconstruct();
67
+ }
68
+
69
+ #initFromScratch({
70
+ locale,
71
+ totalExcl,
72
+ totalIncl,
73
+ date,
74
+ category,
75
+ merchantName,
76
+ time,
77
+ taxes,
78
+ orientation,
79
+ totalTax,
80
+ pageNumber,
81
+ }) {
82
+ this.locale = new Locale(this.constructPrediction(locale));
83
+ this.totalIncl = new Amount(this.constructPrediction(totalIncl));
84
+ this.date = new Date(this.constructPrediction(date));
85
+ this.category = new Field(this.constructPrediction(category));
86
+ this.merchantName = new Field(this.constructPrediction(merchantName));
87
+ this.time = new Field(this.constructPrediction(time));
88
+ if (taxes !== undefined) {
89
+ this.taxes = [];
90
+ for (const t of taxes) {
91
+ this.taxes.push(
92
+ new Tax({
93
+ prediction: { value: t[0], rate: t[1] },
94
+ pageNumber,
95
+ valueKey: "value",
96
+ rateKey: "rate",
97
+ })
98
+ );
99
+ }
100
+ }
101
+ this.orientation = new Orientation(this.constructPrediction(orientation));
102
+ this.totalTax = new Amount(this.constructPrediction(totalTax));
103
+ this.totalExcl = new Amount(this.constructPrediction(totalExcl));
104
+ }
105
+
106
+ /**
107
+ Set the object attributes with api prediction values
108
+ @param apiPrediction: Raw prediction from HTTP response
109
+ @param pageNumber: Page number for multi pages pdf input
110
+ */
111
+ #initFromApiPrediction(apiPrediction, pageNumber) {
112
+ this.words = [];
113
+ this.locale = new Locale({ prediction: apiPrediction.locale, pageNumber });
114
+ this.totalIncl = new Amount({
115
+ prediction: apiPrediction.total_incl,
116
+ valueKey: "value",
117
+ pageNumber,
118
+ });
119
+ this.date = new Date({
120
+ prediction: apiPrediction.date,
121
+ valueKey: "value",
122
+ pageNumber,
123
+ });
124
+ this.category = new Field({
125
+ prediction: apiPrediction.category,
126
+ pageNumber,
127
+ });
128
+ this.merchantName = new Field({
129
+ prediction: apiPrediction.supplier,
130
+ valueKey: "value",
131
+ pageNumber,
132
+ });
133
+ this.time = new Field({
134
+ prediction: apiPrediction.time,
135
+ valueKey: "value",
136
+ pageNumber,
137
+ });
138
+ this.taxes = apiPrediction.taxes.map(
139
+ (taxPrediction) =>
140
+ new Tax({
141
+ prediction: taxPrediction,
142
+ pageNumber,
143
+ valueKey: "value",
144
+ rateKey: "rate",
145
+ codeKey: "code",
146
+ })
147
+ );
148
+ this.totalTax = new Amount({
149
+ prediction: { value: undefined, probability: 0 },
150
+ valueKey: "value",
151
+ pageNumber,
152
+ });
153
+ this.totalExcl = new Amount({
154
+ prediction: { value: undefined, probability: 0 },
155
+ valueKey: "value",
156
+ pageNumber,
157
+ });
158
+ if (this.level === "page") {
159
+ this.orientation = new Orientation({
160
+ prediction: apiPrediction.orientation,
161
+ pageNumber,
162
+ });
163
+ } else {
164
+ this.orientation = new Orientation(
165
+ this.constructPrediction({
166
+ prediction: {
167
+ value: undefined,
168
+ probability: 0.0,
169
+ degrees: undefined,
170
+ },
171
+ })
172
+ );
173
+ }
174
+ if ("mvision" in apiPrediction) this.words = apiPrediction.mvision;
175
+ }
176
+
177
+ toString() {
178
+ return `
179
+ -----Receipt data-----
180
+ Filename: ${this.filename}
181
+ Total amount: ${this.totalIncl.value}
182
+ Date: ${this.date.value}
183
+ Category: ${this.category.value}
184
+ Time: ${this.time.value}
185
+ Merchant name: ${this.merchantName.value}
186
+ Taxes: ${this.taxes.map((tax) => tax.toString()).join(" - ")}
187
+ Total taxes: ${this.totalTax.value}
188
+ `;
189
+ }
190
+
191
+ static async load(path) {
192
+ const file = fs.readFile(path);
193
+ const args = JSON.parse(file);
194
+ return new Receipt({ reconsctruted: true, ...args });
195
+ }
196
+
197
+ /**
198
+ * Call all check methods
199
+ */
200
+ #checklist() {
201
+ this.checklist = { taxesMatchTotalIncl: this.#taxesMatchTotal() };
202
+ }
203
+
204
+ #taxesMatchTotal() {
205
+ // Check taxes and total amount exist
206
+
207
+ if (this.taxes.length === 0 || this.totalIncl.value == null) return false;
208
+
209
+ // Reconstruct total_incl from taxes
210
+ let totalVat = 0;
211
+ let reconstructedTotal = 0;
212
+ this.taxes.forEach((tax) => {
213
+ if (tax.value == null || !tax.rate) return false;
214
+ totalVat += tax.value;
215
+ reconstructedTotal += tax.value + (100 * tax.value) / tax.rate;
216
+ });
217
+
218
+ // Sanity check
219
+ if (totalVat <= 0) return false;
220
+
221
+ // Crate epsilon
222
+ const eps = 1 / (100 * totalVat);
223
+
224
+ if (
225
+ this.totalIncl.value * (1 - eps) - 0.02 <= reconstructedTotal &&
226
+ reconstructedTotal <= this.totalIncl.value * (1 + eps) + 0.02
227
+ ) {
228
+ this.taxes = this.taxes.map((tax) => ({ ...tax, probability: 1.0 }));
229
+ this.totalTax.probability = 1.0;
230
+ this.totalIncl.probability = 1.0;
231
+ return true;
232
+ }
233
+ return false;
234
+ }
235
+
236
+ /**
237
+ * Call all fields that need to be reconstructed
238
+ */
239
+ #reconstruct() {
240
+ this.#reconstructTotalExclFromTCCAndTaxes();
241
+ this.#reconstructTotalTax();
242
+ }
243
+
244
+ /**
245
+ * Set this.totalExcl with Amount object
246
+ * The totalExcl Amount value is the difference between totalIncl and sum of taxes
247
+ * The totalExcl Amount probability is the product of this.taxes probabilities multiplied by totalIncl probability
248
+ */
249
+ #reconstructTotalExclFromTCCAndTaxes() {
250
+ if (this.taxes.length && this.totalIncl.value != null) {
251
+ const totalExcl = {
252
+ value: this.totalIncl.value - Field.arraySum(this.taxes),
253
+ probability:
254
+ Field.arrayProbability(this.taxes) * this.totalIncl.probability,
255
+ };
256
+ this.totalExcl = new Amount({
257
+ prediction: totalExcl,
258
+ valueKey: "value",
259
+ reconstructed: true,
260
+ });
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Set this.totalTax with Amount object
266
+ * The totalTax Amount value is the sum of all this.taxes value
267
+ * The totalTax Amount probability is the product of this.taxes probabilities
268
+ */
269
+ #reconstructTotalTax() {
270
+ if (this.taxes.length && this.totalTax.value == null) {
271
+ const totalTax = {
272
+ value: this.taxes
273
+ .map((tax) => tax.value || 0)
274
+ .reduce((a, b) => a + b, 0),
275
+ probability: Field.arrayProbability(this.taxes),
276
+ };
277
+ if (totalTax.value > 0)
278
+ this.totalTax = new Amount({
279
+ prediction: totalTax,
280
+ valueKey: "value",
281
+ reconstructed: true,
282
+ });
283
+ }
284
+ }
285
+ }
286
+
287
+ module.exports = Receipt;
@@ -0,0 +1,16 @@
1
+ const logger = require("../logger");
2
+
3
+ class ErrorHandler {
4
+ constructor(throwOnError = true) {
5
+ this.throwOnError = throwOnError;
6
+ }
7
+
8
+ throw(error, force = true) {
9
+ if (this.throwOnError || force) throw error;
10
+ else logger.error(error.message);
11
+ }
12
+ }
13
+
14
+ const errorHandler = new ErrorHandler();
15
+
16
+ module.exports = errorHandler;