mindee 1.2.0 → 2.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rubocop.yml +2 -2
  4. data/.yardopts +4 -0
  5. data/CHANGELOG.md +26 -0
  6. data/README.md +46 -23
  7. data/Rakefile +6 -1
  8. data/bin/mindee.rb +78 -61
  9. data/docs/ruby-api-builder.md +124 -0
  10. data/docs/ruby-getting-started.md +265 -0
  11. data/docs/ruby-invoice-ocr.md +260 -0
  12. data/docs/ruby-passport-ocr.md +156 -0
  13. data/docs/ruby-receipt-ocr.md +170 -0
  14. data/lib/mindee/client.rb +132 -93
  15. data/lib/mindee/document_config.rb +29 -169
  16. data/lib/mindee/geometry.rb +105 -8
  17. data/lib/mindee/http/endpoint.rb +80 -0
  18. data/lib/mindee/input/pdf_processing.rb +106 -0
  19. data/lib/mindee/input/sources.rb +97 -0
  20. data/lib/mindee/input.rb +3 -0
  21. data/lib/mindee/parsing/document.rb +31 -0
  22. data/lib/mindee/parsing/error.rb +22 -0
  23. data/lib/mindee/parsing/inference.rb +53 -0
  24. data/lib/mindee/parsing/page.rb +46 -0
  25. data/lib/mindee/parsing/prediction/base.rb +30 -0
  26. data/lib/mindee/{fields → parsing/prediction/common_fields}/amount.rb +5 -1
  27. data/lib/mindee/{fields → parsing/prediction/common_fields}/base.rb +16 -5
  28. data/lib/mindee/{fields → parsing/prediction/common_fields}/company_registration.rb +0 -0
  29. data/lib/mindee/{fields/datefield.rb → parsing/prediction/common_fields/date.rb} +0 -0
  30. data/lib/mindee/{fields → parsing/prediction/common_fields}/locale.rb +0 -0
  31. data/lib/mindee/{fields → parsing/prediction/common_fields}/payment_details.rb +0 -0
  32. data/lib/mindee/parsing/prediction/common_fields/position.rb +39 -0
  33. data/lib/mindee/{fields → parsing/prediction/common_fields}/tax.rb +7 -2
  34. data/lib/mindee/parsing/prediction/common_fields/text.rb +12 -0
  35. data/lib/mindee/parsing/prediction/common_fields.rb +11 -0
  36. data/lib/mindee/parsing/prediction/custom/custom_v1.rb +58 -0
  37. data/lib/mindee/{fields/custom_docs.rb → parsing/prediction/custom/fields.rb} +5 -5
  38. data/lib/mindee/parsing/prediction/eu/license_plate/license_plate_v1.rb +34 -0
  39. data/lib/mindee/parsing/prediction/financial_document/financial_document_v1.rb +245 -0
  40. data/lib/mindee/parsing/prediction/financial_document/invoice_line_item.rb +58 -0
  41. data/lib/mindee/parsing/prediction/fr/bank_account_details/bank_account_details_v1.rb +40 -0
  42. data/lib/mindee/parsing/prediction/fr/carte_vitale/carte_vitale_v1.rb +49 -0
  43. data/lib/mindee/parsing/prediction/fr/id_card/id_card_v1.rb +84 -0
  44. data/lib/mindee/parsing/prediction/invoice/invoice_line_item.rb +58 -0
  45. data/lib/mindee/parsing/prediction/invoice/invoice_v4.rb +216 -0
  46. data/lib/mindee/parsing/prediction/passport/passport_v1.rb +184 -0
  47. data/lib/mindee/parsing/prediction/proof_of_address/proof_of_address_v1.rb +82 -0
  48. data/lib/mindee/parsing/prediction/receipt/receipt_v4.rb +87 -0
  49. data/lib/mindee/parsing/prediction/shipping_container/shipping_container_v1.rb +38 -0
  50. data/lib/mindee/parsing/prediction/us/bank_check/bank_check_v1.rb +70 -0
  51. data/lib/mindee/parsing/prediction.rb +14 -0
  52. data/lib/mindee/parsing.rb +4 -0
  53. data/lib/mindee/version.rb +1 -1
  54. data/mindee.gemspec +2 -1
  55. metadata +60 -24
  56. data/lib/mindee/documents/base.rb +0 -35
  57. data/lib/mindee/documents/custom.rb +0 -65
  58. data/lib/mindee/documents/financial_doc.rb +0 -135
  59. data/lib/mindee/documents/invoice.rb +0 -162
  60. data/lib/mindee/documents/passport.rb +0 -163
  61. data/lib/mindee/documents/receipt.rb +0 -109
  62. data/lib/mindee/documents.rb +0 -7
  63. data/lib/mindee/endpoint.rb +0 -105
  64. data/lib/mindee/fields/orientation.rb +0 -26
  65. data/lib/mindee/fields.rb +0 -11
  66. data/lib/mindee/inputs.rb +0 -153
  67. data/lib/mindee/response.rb +0 -27
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'page'
4
+
5
+ module Mindee
6
+ # Product information
7
+ class Product
8
+ attr_reader :name, :type, :version
9
+
10
+ # @param http_response [Hash]
11
+ def initialize(http_response)
12
+ @name = http_response['name']
13
+ @type = http_response['type']
14
+ @version = http_response['version']
15
+ end
16
+ end
17
+
18
+ # Inference holds all predictions
19
+ class Inference
20
+ # @return [Boolean]
21
+ attr_reader :is_rotation_applied
22
+ # @return [Array<Mindee::Page>]
23
+ attr_reader :pages
24
+ # @return [Mindee::Prediction]
25
+ attr_reader :prediction
26
+ # @return [Mindee::Product]
27
+ attr_reader :product
28
+
29
+ # @param prediction_class [Class<Mindee::Prediction::Prediction>]
30
+ # @param http_response [Hash]
31
+ def initialize(prediction_class, http_response)
32
+ @is_rotation_applied = http_response['is_rotation_applied']
33
+ @prediction = prediction_class.new(http_response['prediction'], nil)
34
+ @product = Product.new(http_response['product'])
35
+ @pages = []
36
+ http_response['pages'].each do |page|
37
+ @pages.push(Page.new(prediction_class, page))
38
+ end
39
+ end
40
+
41
+ def to_s
42
+ is_rotation_applied = @is_rotation_applied ? 'Yes' : 'No'
43
+ out_str = String.new
44
+ out_str << "Inference\n#########"
45
+ out_str << "\n:Product: #{@product.name} v#{@product.version}"
46
+ out_str << "\n:Rotation applied: #{is_rotation_applied}"
47
+ out_str << "\n\nPrediction\n=========="
48
+ out_str << "\n#{@prediction}"
49
+ out_str << "\n\nPage Predictions\n================\n\n"
50
+ out_str << @pages.map(&:to_s).join("\n\n")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mindee
4
+ # Page orientation
5
+ class Orientation
6
+ # @return [Integer]
7
+ attr_reader :page_id
8
+ # A prediction among these 3 possible outputs:
9
+ # * 0 degrees: the page is already upright
10
+ # * 90 degrees: the page must be rotated clockwise to be upright
11
+ # * 270 degrees: the page must be rotated counterclockwise to be upright
12
+ # @return [Integer, nil]
13
+ attr_reader :value
14
+
15
+ # @param prediction [Hash]
16
+ # @param page_id [Integer]
17
+ def initialize(prediction, page_id)
18
+ @value = prediction['value']
19
+ @page_id = page_id
20
+ end
21
+ end
22
+
23
+ # Page object
24
+ class Page
25
+ # @return [Integer]
26
+ attr_reader :id
27
+ # @return [Mindee::Prediction]
28
+ attr_reader :prediction
29
+ # @return [Mindee::Orientation]
30
+ attr_reader :orientation
31
+
32
+ def initialize(prediction_class, page)
33
+ @id = page['id'].to_i
34
+ @prediction = prediction_class.new(page['prediction'], @id)
35
+ @orientation = Orientation.new(page['orientation'], @id)
36
+ end
37
+
38
+ def to_s
39
+ out_str = String.new
40
+ title = "Page #{@id}"
41
+ out_str << "#{title}\n"
42
+ out_str << ('-' * title.size)
43
+ out_str << "\n#{@prediction}"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mindee
4
+ module Prediction
5
+ # Base document object.
6
+ class Prediction
7
+ # document type
8
+ # @return [String]
9
+ attr_reader :document_type
10
+ # Validation checks for the document
11
+ # @return [Hash<Symbol, Boolean>]
12
+ attr_reader :checklist
13
+ # Original filename of the document
14
+ # @return [String, nil]
15
+ attr_reader :filename
16
+ # Detected MIME type of the document
17
+ # @return [String, nil]
18
+ attr_reader :file_mimetype
19
+
20
+ def initialize(_prediction, _page_id)
21
+ @checklist = {}
22
+ end
23
+
24
+ # @return [Boolean]
25
+ def all_checks
26
+ @checklist.all? { |_, value| value == true }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,7 +4,7 @@ require_relative 'base'
4
4
 
5
5
  module Mindee
6
6
  # Represents tax information.
7
- class Amount < Field
7
+ class AmountField < Field
8
8
  # Amount value as 3 decimal float
9
9
  # @return [Float, nil]
10
10
  attr_reader :value
@@ -13,5 +13,9 @@ module Mindee
13
13
  super
14
14
  @value = @value.round(3) unless @value.nil?
15
15
  end
16
+
17
+ def to_s
18
+ Field.float_to_string(@value)
19
+ end
16
20
  end
17
21
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../geometry'
3
+ require_relative '../../../geometry'
4
4
 
5
5
  module Mindee
6
6
  # Base field object.
@@ -8,8 +8,8 @@ module Mindee
8
8
  # @return [String, Float, Integer, Boolean]
9
9
  attr_reader :value
10
10
  # @return [Array<Array<Float>>]
11
- attr_reader :bbox
12
- # @return [Array<Array<Float>>]
11
+ attr_reader :bounding_box
12
+ # @return [Mindee::Geometry::Polygon]
13
13
  attr_reader :polygon
14
14
  # @return [Integer, nil]
15
15
  attr_reader :page_id
@@ -26,8 +26,8 @@ module Mindee
26
26
  def initialize(prediction, page_id, reconstructed: false)
27
27
  @value = prediction['value']
28
28
  @confidence = prediction['confidence']
29
- @polygon = prediction['polygon']
30
- @bbox = Geometry.get_bbox_as_polygon(@polygon) unless @polygon.nil? || @polygon.empty?
29
+ @polygon = Geometry.polygon_from_prediction(prediction['polygon'])
30
+ @bounding_box = Geometry.get_bounding_box(@polygon) unless @polygon.nil? || @polygon.empty?
31
31
  @page_id = page_id || prediction['page_id']
32
32
  @reconstructed = reconstructed
33
33
  end
@@ -57,5 +57,16 @@ module Mindee
57
57
  end
58
58
  arr_sum.to_f
59
59
  end
60
+
61
+ # @param value [Float]
62
+ # @param min_precision [Integer]
63
+ def self.float_to_string(value, min_precision = 2)
64
+ return String.new if value.nil?
65
+
66
+ precision = value.to_f.to_s.split('.')[1].size
67
+ precision = [precision, min_precision].max
68
+ format_string = "%.#{precision}f"
69
+ format(format_string, value)
70
+ end
60
71
  end
61
72
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mindee
4
+ # An element's position on the image
5
+ class PositionField
6
+ # @return [Mindee::Geometry::Polygon]
7
+ attr_reader :polygon
8
+ # @return [Mindee::Geometry::Polygon]
9
+ attr_reader :value
10
+ # @return [Mindee::Geometry::Quadrilateral]
11
+ attr_reader :quadrangle
12
+ # @return [Mindee::Geometry::Quadrilateral]
13
+ attr_reader :rectangle
14
+ # @return [Mindee::Geometry::Quadrilateral]
15
+ attr_reader :bounding_box
16
+
17
+ # @param prediction [Hash]
18
+ # @param page_id [Integer, nil]
19
+ def initialize(prediction, page_id)
20
+ @polygon = Geometry.polygon_from_prediction(prediction['polygon']) unless prediction['polygon'].empty?
21
+ @quadrangle = to_quadrilateral(prediction, 'quadrangle')
22
+ @rectangle = to_quadrilateral(prediction, 'rectangle')
23
+ @bounding_box = to_quadrilateral(prediction, 'bounding_box')
24
+ @page_id = page_id || prediction['page_id']
25
+ @value = @polygon
26
+ end
27
+
28
+ def to_s
29
+ out_str = String.new
30
+ out_str << "Polygon with #{@polygon.size} points."
31
+ end
32
+
33
+ private
34
+
35
+ def to_quadrilateral(prediction, key)
36
+ Geometry.quadrilateral_from_prediction(prediction[key]) unless prediction[key].empty?
37
+ end
38
+ end
39
+ end
@@ -24,10 +24,15 @@ module Mindee
24
24
  @code = prediction['code'] unless prediction['code'] == 'None'
25
25
  end
26
26
 
27
+ # @param value [Float]
28
+ def print_float(value)
29
+ format('%.2f', value)
30
+ end
31
+
27
32
  def to_s
28
33
  out_str = String.new
29
- out_str << "#{@value} " if @value
30
- out_str << "#{@rate}% " if @rate
34
+ out_str << "#{print_float(@value)} " if @value
35
+ out_str << "#{print_float(@rate)}% " if @rate
31
36
  out_str << "#{@code} " if @code
32
37
  out_str.strip
33
38
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Mindee
6
+ # Represents tax information.
7
+ class TextField < Field
8
+ # Amount value as 3 decimal float
9
+ # @return [String, nil]
10
+ attr_reader :value
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'common_fields/amount'
4
+ require_relative 'common_fields/base'
5
+ require_relative 'common_fields/company_registration'
6
+ require_relative 'common_fields/date'
7
+ require_relative 'common_fields/locale'
8
+ require_relative 'common_fields/payment_details'
9
+ require_relative 'common_fields/position'
10
+ require_relative 'common_fields/tax'
11
+ require_relative 'common_fields/text'
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fields'
4
+ require_relative '../base'
5
+
6
+ module Mindee
7
+ module Prediction
8
+ # Custom document object.
9
+ class CustomV1 < Prediction
10
+ # All value fields in the document
11
+ # @return [Hash<Symbol, Mindee::ListField>]
12
+ attr_reader :fields
13
+ # All classifications in the document
14
+ # @return [Hash<Symbol, Mindee::ClassificationField>]
15
+ attr_reader :classifications
16
+
17
+ # @param prediction [Hash]
18
+ # @param page_id [Integer, nil]
19
+ def initialize(prediction, page_id)
20
+ super
21
+ @fields = {}
22
+ @classifications = {}
23
+ prediction.each do |field_name, field_prediction|
24
+ field_sym = field_name.to_sym
25
+ set_field(field_sym, field_prediction, page_id)
26
+ end
27
+ end
28
+
29
+ def to_s
30
+ out_str = String.new
31
+ @classifications.each do |name, info|
32
+ out_str << "\n:#{name}: #{info}".rstrip
33
+ end
34
+ @fields.each do |name, info|
35
+ out_str << "\n:#{name}: #{info}".rstrip
36
+ end
37
+ out_str[1..].to_s
38
+ end
39
+
40
+ private
41
+
42
+ # @param field_prediction [Hash]
43
+ def set_field(field_sym, field_prediction, page_id)
44
+ # Currently two types of fields possible in a custom API response:
45
+ # fields having a list of values, and classification fields.
46
+ # Here we use the fact that only value lists have the 'values' attribute.
47
+
48
+ if field_prediction.key? 'values'
49
+ @fields[field_sym] = ListField.new(field_prediction, page_id)
50
+ elsif field_prediction.key? 'value'
51
+ @classifications[field_sym] = ClassificationField.new(field_prediction)
52
+ else
53
+ throw 'Unknown API field type'
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -26,9 +26,9 @@ module Mindee
26
26
  # The confidence score, value will be between 0.0 and 1.0
27
27
  # @return [Float]
28
28
  attr_accessor :confidence
29
- # @return [Array<Array<Float>>]
30
- attr_reader :bbox
31
- # @return [Array<Array<Float>>]
29
+ # @return [Mindee::Geometry::Quadrilateral]
30
+ attr_reader :bounding_box
31
+ # @return [Mindee::Geometry::Polygon]
32
32
  attr_reader :polygon
33
33
  attr_reader :content
34
34
 
@@ -36,8 +36,8 @@ module Mindee
36
36
  def initialize(prediction)
37
37
  @content = prediction['content']
38
38
  @confidence = prediction['confidence']
39
- @polygon = prediction['polygon']
40
- @bbox = Geometry.get_bbox_as_polygon(@polygon) unless @polygon.nil? || @polygon.empty?
39
+ @polygon = Geometry.polygon_from_prediction(prediction['polygon'])
40
+ @bounding_box = Geometry.get_bounding_box(@polygon) unless @polygon.nil? || @polygon.empty?
41
41
  end
42
42
 
43
43
  # @return [String]
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../common_fields'
4
+ require_relative '../../base'
5
+
6
+ module Mindee
7
+ module Prediction
8
+ module EU
9
+ # License plate prediction.
10
+ class LicensePlateV1 < Prediction
11
+ # A list of license plates.
12
+ # @return [Array<Mindee::TextField>]
13
+ attr_reader :license_plates
14
+
15
+ # @param prediction [Hash]
16
+ # @param page_id [Integer, nil]
17
+ def initialize(prediction, page_id)
18
+ super
19
+ @license_plates = []
20
+ prediction['license_plates'].each do |item|
21
+ @license_plates.push(TextField.new(item, page_id))
22
+ end
23
+ end
24
+
25
+ def to_s
26
+ license_plates = @license_plates.map(&:value).join(', ')
27
+ out_str = String.new
28
+ out_str << "\n:License plates: #{license_plates}".rstrip
29
+ out_str[1..].to_s
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../common_fields'
4
+ require_relative '../base'
5
+ require_relative 'invoice_line_item'
6
+
7
+ module Mindee
8
+ module Prediction
9
+ # Invoice document.
10
+ class FinancialDocumentV1 < Prediction
11
+ # Locale information.
12
+ # @return [Mindee::Locale]
13
+ attr_reader :locale
14
+ # The nature of the invoice.
15
+ # @return [Mindee::TextField]
16
+ attr_reader :document_type
17
+ # The total amount with tax included.
18
+ # @return [Mindee::AmountField]
19
+ attr_reader :total_amount
20
+ # The total amount without the tax value.
21
+ # @return [Mindee::AmountField]
22
+ attr_reader :total_net
23
+ # The total tax.
24
+ # @return [Mindee::AmountField]
25
+ attr_reader :total_tax
26
+ # The creation date of the invoice.
27
+ # @return [Mindee::DateField]
28
+ attr_reader :date
29
+ # The invoice number.
30
+ # @return [Mindee::TextField]
31
+ attr_reader :invoice_number
32
+ # List of Reference numbers including PO number.
33
+ # @return [Mindee::TextField]
34
+ attr_reader :reference_numbers
35
+ # The due date of the invoice.
36
+ # @return [Mindee::DateField]
37
+ attr_reader :due_date
38
+ # The list of taxes.
39
+ # @return [Array<Mindee::TaxField>]
40
+ attr_reader :taxes
41
+ # The name of the customer.
42
+ # @return [Mindee::TextField]
43
+ attr_reader :customer_name
44
+ # The address of the customer.
45
+ # @return [Mindee::TextField]
46
+ attr_reader :customer_address
47
+ # The company registration information for the customer.
48
+ # @return [Array<Mindee::CompanyRegistration>]
49
+ attr_reader :customer_company_registrations
50
+ # The supplier's name.
51
+ # @return [Mindee::TextField]
52
+ attr_reader :supplier_name
53
+ # The supplier's address.
54
+ # @return [Mindee::TextField]
55
+ attr_reader :supplier_address
56
+ # The payment information.
57
+ # @return [Array<Mindee::PaymentDetails>]
58
+ attr_reader :supplier_payment_details
59
+ # The supplier's company registration information.
60
+ # @return [Array<Mindee::CompanyRegistration>]
61
+ attr_reader :supplier_company_registrations
62
+ # Line items details.
63
+ # @return [Array<Mindee::InvoiceLineItem>]
64
+ attr_reader :line_items
65
+ # Time as seen on the receipt in HH:MM format.
66
+ # @return [Mindee::TextField]
67
+ attr_reader :time
68
+ # The receipt category among predefined classes.
69
+ # @return [Mindee::TextField]
70
+ attr_reader :category
71
+ # The receipt sub-category among predefined classes.
72
+ # @return [Mindee::TextField]
73
+ attr_reader :subcategory
74
+ # A classification field, that can return 4 values : 'EXPENSE RECEIPT' ,
75
+ # 'CREDIT CARD RECEIPT', 'INVOICE', 'CREDIT NOTE'
76
+ # @return [Mindee::TextField]
77
+ attr_reader :document_type # rubocop:todo Lint/DuplicateMethods
78
+ # Total amount of tip and gratuity. Both typed and handwritten characters are supported.
79
+ # @return [Mindee::AmountField]
80
+ attr_reader :tip
81
+
82
+ # @param prediction [Hash]
83
+ # @param page_id [Integer, nil]
84
+ def initialize(prediction, page_id) # rubocop:todo Metrics/AbcSize
85
+ super
86
+
87
+ @time = TextField.new(prediction['time'], page_id)
88
+ @category = TextField.new(prediction['category'], page_id)
89
+ @subcategory = TextField.new(prediction['subcategory'], page_id)
90
+ @document_type = TextField.new(prediction['document_type'], page_id)
91
+ @tip = AmountField.new(prediction['tip'], page_id)
92
+ @locale = Locale.new(prediction['locale'])
93
+ @document_type = TextField.new(prediction['document_type'], page_id)
94
+ @total_amount = AmountField.new(prediction['total_amount'], page_id)
95
+ @total_net = AmountField.new(prediction['total_net'], page_id)
96
+ @customer_address = TextField.new(prediction['customer_address'], page_id)
97
+ @customer_name = TextField.new(prediction['customer_name'], page_id)
98
+ @date = DateField.new(prediction['date'], page_id)
99
+ @due_date = DateField.new(prediction['due_date'], page_id)
100
+ @invoice_number = TextField.new(prediction['invoice_number'], page_id)
101
+ @supplier_name = TextField.new(prediction['supplier_name'], page_id)
102
+ @supplier_address = TextField.new(prediction['supplier_address'], page_id)
103
+
104
+ @reference_numbers = []
105
+ prediction['reference_numbers'].each do |item|
106
+ @reference_numbers.push(TextField.new(item, page_id))
107
+ end
108
+ @customer_company_registrations = []
109
+ prediction['customer_company_registrations'].each do |item|
110
+ @customer_company_registrations.push(CompanyRegistration.new(item, page_id))
111
+ end
112
+ @taxes = []
113
+ prediction['taxes'].each do |item|
114
+ @taxes.push(TaxField.new(item, page_id))
115
+ end
116
+ @supplier_payment_details = []
117
+ prediction['supplier_payment_details'].each do |item|
118
+ @supplier_payment_details.push(PaymentDetails.new(item, page_id))
119
+ end
120
+ @supplier_company_registrations = []
121
+ prediction['supplier_company_registrations'].each do |item|
122
+ @supplier_company_registrations.push(CompanyRegistration.new(item, page_id))
123
+ end
124
+
125
+ @total_tax = AmountField.new(
126
+ { value: nil, confidence: 0.0 }, page_id
127
+ )
128
+
129
+ @line_items = []
130
+ prediction['line_items'].each do |item|
131
+ @line_items.push(InvoiceLineItem.new(item, page_id))
132
+ end
133
+ reconstruct(page_id)
134
+ end
135
+
136
+ def to_s
137
+ customer_company_registrations = @customer_company_registrations.map(&:value).join('; ')
138
+ supplier_payment_details = @supplier_payment_details.map(&:to_s).join("\n ")
139
+ supplier_company_registrations = @supplier_company_registrations.map(&:to_s).join('; ')
140
+ reference_numbers = @reference_numbers.map(&:to_s).join(', ')
141
+ taxes = @taxes.join("\n ")
142
+ out_str = String.new
143
+ out_str << "\n:Document type: #{@document_type}".rstrip
144
+ out_str << "\n:Category: #{@category}".rstrip
145
+ out_str << "\n:Subcategory: #{@subcategory}".rstrip
146
+ out_str << "\n:Locale: #{@locale}".rstrip
147
+ out_str << "\n:Date: #{@date}".rstrip
148
+ out_str << "\n:Due date: #{@due_date}".rstrip
149
+ out_str << "\n:Time: #{@time}".rstrip
150
+ out_str << "\n:Number: #{@invoice_number}".rstrip
151
+ out_str << "\n:Reference numbers: #{reference_numbers}".rstrip
152
+ out_str << "\n:Supplier name: #{@supplier_name}".rstrip
153
+ out_str << "\n:Supplier address: #{@supplier_address}".rstrip
154
+ out_str << "\n:Supplier company registrations: #{supplier_company_registrations}".rstrip
155
+ out_str << "\n:Supplier payment details: #{supplier_payment_details}".rstrip
156
+
157
+ out_str << "\n:Customer name: #{@customer_name}".rstrip
158
+ out_str << "\n:Customer address: #{@customer_address}".rstrip
159
+ out_str << "\n:Customer company registrations: #{customer_company_registrations}".rstrip
160
+
161
+ out_str << "\n:Tip: #{@tip}".rstrip
162
+
163
+ out_str << "\n:Taxes: #{taxes}".rstrip
164
+ out_str << "\n:Total taxes: #{@total_tax}".rstrip
165
+ out_str << "\n:Total net: #{@total_net}".rstrip
166
+ out_str << "\n:Total amount: #{@total_amount}".rstrip
167
+
168
+ out_str << line_items_to_s
169
+
170
+ out_str[1..].to_s
171
+ end
172
+
173
+ private
174
+
175
+ def line_items_to_s
176
+ line_item_separator = "#{'=' * 22} #{'=' * 8} #{'=' * 9} #{'=' * 10} #{'=' * 18} #{'=' * 36}"
177
+ line_items = @line_items.map(&:to_s).join("\n")
178
+
179
+ out_str = String.new
180
+ out_str << "\n\n:Line Items:"
181
+
182
+ return out_str if line_items.empty?
183
+
184
+ out_str << "\n#{line_item_separator}"
185
+ out_str << "\nCode QTY Price Amount Tax (Rate) Description"
186
+ out_str << "\n#{line_item_separator}"
187
+ out_str << "\n#{line_items}"
188
+ out_str << "\n#{line_item_separator}"
189
+ end
190
+
191
+ def reconstruct(page_id)
192
+ construct_total_tax_from_taxes(page_id)
193
+ return unless page_id.nil?
194
+
195
+ construct_total_excl_from_tcc_and_taxes(page_id)
196
+ construct_total_incl_from_taxes_plus_excl(page_id)
197
+ construct_total_tax_from_totals(page_id)
198
+ end
199
+
200
+ def construct_total_excl_from_tcc_and_taxes(page_id)
201
+ return if @total_amount.value.nil? || taxes.empty? || !@total_net.value.nil?
202
+
203
+ total_excl = {
204
+ 'value' => @total_amount.value - @taxes.map(&:value).sum,
205
+ 'confidence' => TextField.array_confidence(@taxes) * @total_amount.confidence,
206
+ }
207
+ @total_net = AmountField.new(total_excl, page_id, reconstructed: true)
208
+ end
209
+
210
+ def construct_total_incl_from_taxes_plus_excl(page_id)
211
+ return if @total_net.value.nil? || @taxes.empty? || !@total_amount.value.nil?
212
+
213
+ total_incl = {
214
+ 'value' => @taxes.map(&:value).sum + @total_net.value,
215
+ 'confidence' => TextField.array_confidence(@taxes) * @total_net.confidence,
216
+ }
217
+ @total_amount = AmountField.new(total_incl, page_id, reconstructed: true)
218
+ end
219
+
220
+ def construct_total_tax_from_taxes(page_id)
221
+ return if @taxes.empty?
222
+
223
+ total_tax = {
224
+ 'value' => @taxes.map(&:value).sum,
225
+ 'confidence' => TextField.array_confidence(@taxes),
226
+ }
227
+ return unless total_tax['value'].positive?
228
+
229
+ @total_tax = AmountField.new(total_tax, page_id, reconstructed: true)
230
+ end
231
+
232
+ def construct_total_tax_from_totals(page_id)
233
+ return if !@total_tax.value.nil? || @total_amount.value.nil? || @total_net.value.nil?
234
+
235
+ total_tax = {
236
+ 'value' => @total_amount.value - @total_net.value,
237
+ 'confidence' => TextField.array_confidence(@taxes),
238
+ }
239
+ return unless total_tax['value'] >= 0
240
+
241
+ @total_tax = AmountField.new(total_tax, page_id, reconstructed: true)
242
+ end
243
+ end
244
+ end
245
+ end