Skip to content

Commit 3b1d68d

Browse files
authored
✨ add support for Receipts v5 (#23)
1 parent 74acebe commit 3b1d68d

File tree

13 files changed

+272
-30
lines changed

13 files changed

+272
-30
lines changed

bin/mindee.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
},
2525
"receipt" => {
2626
help: "Expense Receipt",
27-
prediction: Mindee::Prediction::ReceiptV4,
27+
prediction: Mindee::Prediction::ReceiptV5,
2828
},
2929
"passport" => {
3030
help: "Passport",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require 'mindee'
2+
3+
# Init a new client, specifying an API key
4+
mindee_client = Mindee::Client.new(api_key: 'my-api-key')
5+
6+
# Load a file from disk and parse it
7+
result = mindee_client.doc_from_path('/path/to/the/file.ext')
8+
.parse(Mindee::Prediction::ReceiptV5)
9+
10+
# Print a full summary of the parsed data in RST format
11+
puts result
12+
13+
# Print the document-level parsed data
14+
# puts result.inference.prediction

docs/ruby-receipt-ocr.md

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ The Ruby OCR SDK supports the [receipt API](https://developers.mindee.com/docs/
22

33
Using this sample below, we are going to illustrate how to extract the data that we want using the OCR SDK.
44

5-
![sample receipt](https://raw.githubusercontent.com/mindee/client-lib-test-data/main/receipt/receipt-with-tip.jpg)
5+
![sample receipt](https://raw.githubusercontent.com/mindee/client-lib-test-data/main/receipt/receipt.jpg)
66

77
## Quick Start
88
```ruby
@@ -11,27 +11,41 @@ require 'mindee'
1111
# Init a new client, specifying an API key
1212
mindee_client = Mindee::Client.new(api_key: 'my-api-key')
1313

14-
# Send the file
15-
result = mindee_client.doc_from_path('/path/to/the/file.ext').parse(Mindee::Prediction::ReceiptV4)
14+
# Load a file from disk and parse it
15+
result = mindee_client.doc_from_path('/path/to/the/file.ext')
16+
.parse(Mindee::Prediction::ReceiptV5)
1617

17-
# Print a summary of the document prediction in RST format
18+
# Print a full summary of the parsed data in RST format
19+
# puts result
20+
21+
# Print the document-level parsed data
1822
puts result.inference.prediction
1923
```
2024

2125
Output:
2226
```
23-
:Locale: en-US; en; US; USD;
24-
:Date: 2014-07-07
25-
:Category: food
26-
:Subcategory: restaurant
27-
:Document type: EXPENSE RECEIPT
28-
:Time: 20:20
29-
:Supplier name: LOGANS
30-
:Taxes: 3.34 TAX
31-
:Total net: 40.48
32-
:Total taxes: 3.34
33-
:Tip: 10.00
34-
:Total amount: 53.8
27+
:Expense Locale: en-GB; en; GB; GBP;
28+
:Expense Category: food
29+
:Expense Sub Category: restaurant
30+
:Document Type: EXPENSE RECEIPT
31+
:Purchase Date: 2016-02-26
32+
:Purchase Time: 15:20
33+
:Total Amount: 10.20
34+
:Total Excluding Taxes: 8.50
35+
:Total Tax: 1.70
36+
:Tip and Gratuity:
37+
:Taxes: 1.70 20.00% VAT
38+
:Supplier Name: CLACHAN
39+
:Supplier Company Registrations: 232153895
40+
232153895
41+
:Supplier Address: 34 kingley street w1b 5qh
42+
:Supplier Phone Number: 02074940834
43+
:Line Items:
44+
+--------------------------------------+----------+--------------+------------+
45+
| Description | Quantity | Total Amount | Unit Price |
46+
+======================================+==========+==============+============+
47+
| Meantime Pale | 2.00 | 10.20 | |
48+
+--------------------------------------+----------+--------------+------------+
3549
```
3650

3751
## Fields
@@ -50,7 +64,6 @@ Depending on the field type specified, additional attributes can be extracted in
5064

5165
Using the above sample, the following are the basic fields that can be extracted:
5266

53-
- [Orientation](#orientation)
5467
- [Category](#category)
5568
- [Date](#date)
5669
- [Locale](#locale)

lib/mindee/client.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ def init_default_endpoints
183183
@doc_configs[['mindee', Prediction::ReceiptV4.name]] = standard_document_config(
184184
Prediction::ReceiptV4, 'expense_receipts', '4'
185185
)
186+
@doc_configs[['mindee', Prediction::ReceiptV5.name]] = standard_document_config(
187+
Prediction::ReceiptV5, 'expense_receipts', '5'
188+
)
186189
@doc_configs[['mindee', Prediction::PassportV1.name]] = standard_document_config(
187190
Prediction::PassportV1, 'passport', '1'
188191
)

lib/mindee/parsing/prediction.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require_relative 'prediction/invoice/invoice_v4'
77
require_relative 'prediction/passport/passport_v1'
88
require_relative 'prediction/receipt/receipt_v4'
9+
require_relative 'prediction/receipt/receipt_v5'
910
require_relative 'prediction/eu/license_plate/license_plate_v1'
1011
require_relative 'prediction/shipping_container/shipping_container_v1'
1112
require_relative 'prediction/us/bank_check/bank_check_v1'

lib/mindee/parsing/prediction/financial_document/financial_document_v1.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require_relative '../common_fields'
44
require_relative '../base'
5-
require_relative 'line_item'
5+
require_relative 'financial_document_v1_line_item'
66

77
module Mindee
88
module Prediction
@@ -60,7 +60,7 @@ class FinancialDocumentV1 < Prediction
6060
# @return [Array<Mindee::CompanyRegistration>]
6161
attr_reader :supplier_company_registrations
6262
# Line items details.
63-
# @return [Array<Mindee::FinancialDocumentLineItem>]
63+
# @return [Array<Mindee::FinancialDocumentV1LineItem>]
6464
attr_reader :line_items
6565
# Time as seen on the receipt in HH:MM format.
6666
# @return [Mindee::TextField]
@@ -124,7 +124,7 @@ def initialize(prediction, page_id) # rubocop:todo Metrics/AbcSize
124124
)
125125
@line_items = []
126126
prediction['line_items'].each do |item|
127-
@line_items.push(FinancialDocumentLineItem.new(item, page_id))
127+
@line_items.push(FinancialDocumentV1LineItem.new(item, page_id))
128128
end
129129
reconstruct(page_id)
130130
end

lib/mindee/parsing/prediction/invoice/line_item.rb renamed to lib/mindee/parsing/prediction/financial_document/financial_document_v1_line_item.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
module Mindee
66
# Line items for invoices
7-
class InvoiceLineItem
7+
class FinancialDocumentV1LineItem
88
# @return [String] The product code referring to the item.
99
attr_reader :product_code
1010
# @return [String]
@@ -39,6 +39,7 @@ def initialize(prediction, page_id)
3939
@page_id = page_id
4040
end
4141

42+
# @return String
4243
def to_s
4344
tax = Field.float_to_string(@tax_amount)
4445
tax << " (#{Field.float_to_string(@tax_rate)}%)" unless @tax_rate.nil?
@@ -50,7 +51,8 @@ def to_s
5051
out_str << " #{format('| %- 7s', Field.float_to_string(@unit_price))}"
5152
out_str << " #{format('| %- 8s', Field.float_to_string(@total_amount))}"
5253
out_str << " #{format('| %- 16s', tax)}"
53-
out_str << " #{format('| %- 37s|', description)}"
54+
out_str << " #{format('| %- 37s', description)}"
55+
out_str << '|'
5456
end
5557
end
5658
end

lib/mindee/parsing/prediction/invoice/invoice_v4.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# frozen_string_literal: true
22

33
require_relative '../common_fields'
4-
require_relative 'line_item'
54
require_relative '../base'
5+
require_relative 'invoice_v4_line_item'
66

77
module Mindee
88
module Prediction
@@ -60,7 +60,7 @@ class InvoiceV4 < Prediction
6060
# @return [Array<Mindee::CompanyRegistration>]
6161
attr_reader :supplier_company_registrations
6262
# Line items details.
63-
# @return [Array<Mindee::InvoiceLineItem>]
63+
# @return [Array<Mindee::InvoiceV4LineItem>]
6464
attr_reader :line_items
6565

6666
# @param prediction [Hash]
@@ -103,7 +103,7 @@ def initialize(prediction, page_id)
103103
)
104104
@line_items = []
105105
prediction['line_items'].each do |item|
106-
@line_items.push(InvoiceLineItem.new(item, page_id))
106+
@line_items.push(InvoiceV4LineItem.new(item, page_id))
107107
end
108108
reconstruct(page_id)
109109
end
@@ -149,7 +149,7 @@ def line_items_to_s
149149
line_items = @line_items.map(&:to_s).join("\n#{line_item_separator('-')}\n ")
150150
out_str = String.new
151151
out_str << "\n#{line_item_separator('-')}"
152-
out_str << "\n | Code#{' ' * 17}| QTY | Price | Amount | Tax (Rate) | Description#{' ' * 26}|"
152+
out_str << "\n | Code#{' ' * 17}| QTY | Price | Amount | Tax (Rate) | Description #{' ' * 25}|"
153153
out_str << "\n#{line_item_separator('=')}"
154154
out_str << "\n #{line_items}"
155155
out_str << "\n#{line_item_separator('-')}"

lib/mindee/parsing/prediction/financial_document/line_item.rb renamed to lib/mindee/parsing/prediction/invoice/invoice_v4_line_item.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
module Mindee
66
# Line items for invoices
7-
class FinancialDocumentLineItem
7+
class InvoiceV4LineItem
88
# @return [String] The product code referring to the item.
99
attr_reader :product_code
1010
# @return [String]
@@ -39,6 +39,7 @@ def initialize(prediction, page_id)
3939
@page_id = page_id
4040
end
4141

42+
# @return String
4243
def to_s
4344
tax = Field.float_to_string(@tax_amount)
4445
tax << " (#{Field.float_to_string(@tax_rate)}%)" unless @tax_rate.nil?
@@ -50,7 +51,8 @@ def to_s
5051
out_str << " #{format('| %- 7s', Field.float_to_string(@unit_price))}"
5152
out_str << " #{format('| %- 8s', Field.float_to_string(@total_amount))}"
5253
out_str << " #{format('| %- 16s', tax)}"
53-
out_str << " #{format('| %- 37s|', description)}"
54+
out_str << " #{format('| %- 37s', description)}"
55+
out_str << '|'
5456
end
5557
end
5658
end
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# frozen_string_literal: true
2+
3+
require_relative '../common_fields'
4+
require_relative '../base'
5+
require_relative 'receipt_v5_line_item'
6+
7+
module Mindee
8+
module Prediction
9+
# Expense Receipt v5 prediction results.
10+
class ReceiptV5 < Prediction
11+
# The locale identifier in BCP 47 (RFC 5646) format: ISO language code, '-', ISO country code.
12+
# @return [Mindee::Locale]
13+
attr_reader :locale
14+
# The receipt category among predefined classes.
15+
# @return [Mindee::TextField]
16+
attr_reader :category
17+
# The receipt sub category among predefined classes for transport and food.
18+
# @return [Mindee::TextField]
19+
attr_reader :subcategory
20+
# Whether the document is an expense receipt or a credit card receipt.
21+
# @return [Mindee::TextField]
22+
attr_reader :document_type
23+
# The date the purchase was made.
24+
# @return [Mindee::DateField]
25+
attr_reader :date
26+
# Time of purchase with 24 hours formatting (HH:MM).
27+
# @return [Mindee::TextField]
28+
attr_reader :time
29+
# The total amount paid including taxes, discounts, fees, tips, and gratuity.
30+
# @return [Mindee::AmountField]
31+
attr_reader :total_amount
32+
# The total amount excluding taxes.
33+
# @return [Mindee::AmountField]
34+
attr_reader :total_net
35+
# The total amount of taxes.
36+
# @return [Mindee::AmountField]
37+
attr_reader :total_tax
38+
# The total amount of tip and gratuity.
39+
# @return [Mindee::AmountField]
40+
attr_reader :tip
41+
# List of tax lines information including: Amount, tax rate, tax base amount and tax code.
42+
# @return [Array<Mindee::TaxField>]
43+
attr_reader :taxes
44+
# The name of the supplier or merchant.
45+
# @return [Mindee::TextField]
46+
attr_reader :supplier_name
47+
# List of supplier company registrations or identifiers.
48+
# @return [Array<Mindee::CompanyRegistration>]
49+
attr_reader :supplier_company_registrations
50+
# The address of the supplier or merchant returned as a single string.
51+
# @return [Mindee::TextField]
52+
attr_reader :supplier_address
53+
# The Phone number of the supplier or merchant returned as a single string.
54+
# @return [Mindee::TextField]
55+
attr_reader :supplier_phone_number
56+
# Full extraction of lines, including: description, quantity, unit price and total.
57+
# @return [Array<Mindee::ReceiptV5LineItem>]
58+
attr_reader :line_items
59+
60+
# @param prediction [Hash]
61+
# @param page_id [Integer, nil]
62+
def initialize(prediction, page_id)
63+
super
64+
@locale = Locale.new(prediction['locale'], page_id)
65+
@category = TextField.new(prediction['category'], page_id)
66+
@subcategory = TextField.new(prediction['subcategory'], page_id)
67+
@document_type = TextField.new(prediction['document_type'], page_id)
68+
@date = DateField.new(prediction['date'], page_id)
69+
@time = TextField.new(prediction['time'], page_id)
70+
@total_amount = AmountField.new(prediction['total_amount'], page_id)
71+
@total_net = AmountField.new(prediction['total_net'], page_id)
72+
@total_tax = AmountField.new(prediction['total_tax'], page_id)
73+
@tip = AmountField.new(prediction['tip'], page_id)
74+
@taxes = []
75+
prediction['taxes'].each do |item|
76+
@taxes.push(TaxField.new(item, page_id))
77+
end
78+
@supplier_name = TextField.new(prediction['supplier_name'], page_id)
79+
@supplier_company_registrations = []
80+
prediction['supplier_company_registrations'].each do |item|
81+
@supplier_company_registrations.push(CompanyRegistration.new(item, page_id))
82+
end
83+
@supplier_address = TextField.new(prediction['supplier_address'], page_id)
84+
@supplier_phone_number = TextField.new(prediction['supplier_phone_number'], page_id)
85+
@line_items = []
86+
prediction['line_items'].each do |item|
87+
@line_items.push(ReceiptV5LineItem.new(item, page_id))
88+
end
89+
end
90+
91+
# @return String
92+
def to_s
93+
taxes = @taxes.join("\n #{' ' * 7}")
94+
supplier_company_registrations = @supplier_company_registrations.join("\n #{' ' * 32}")
95+
line_items = line_items_to_s
96+
out_str = String.new
97+
out_str << "\n:Expense Locale: #{@locale}".rstrip
98+
out_str << "\n:Expense Category: #{@category}".rstrip
99+
out_str << "\n:Expense Sub Category: #{@subcategory}".rstrip
100+
out_str << "\n:Document Type: #{@document_type}".rstrip
101+
out_str << "\n:Purchase Date: #{@date}".rstrip
102+
out_str << "\n:Purchase Time: #{@time}".rstrip
103+
out_str << "\n:Total Amount: #{@total_amount}".rstrip
104+
out_str << "\n:Total Excluding Taxes: #{@total_net}".rstrip
105+
out_str << "\n:Total Tax: #{@total_tax}".rstrip
106+
out_str << "\n:Tip and Gratuity: #{@tip}".rstrip
107+
out_str << "\n:Taxes: #{taxes}".rstrip
108+
out_str << "\n:Supplier Name: #{@supplier_name}".rstrip
109+
out_str << "\n:Supplier Company Registrations: #{supplier_company_registrations}".rstrip
110+
out_str << "\n:Supplier Address: #{@supplier_address}".rstrip
111+
out_str << "\n:Supplier Phone Number: #{@supplier_phone_number}".rstrip
112+
out_str << "\n:Line Items:"
113+
out_str << line_items
114+
out_str[1..].to_s
115+
end
116+
117+
private
118+
119+
def line_items_separator(char)
120+
" +#{char * 38}+#{char * 10}+#{char * 14}+#{char * 12}+"
121+
end
122+
123+
def line_items_to_s
124+
return '' if @line_items.empty?
125+
126+
line_items = @line_items.map(&:to_s).join("\n#{line_items_separator('-')}\n ")
127+
out_str = String.new
128+
out_str << "\n#{line_items_separator('-')}"
129+
out_str << "\n | Description #{' ' * 25}| Quantity | Total Amount | Unit Price |"
130+
out_str << "\n#{line_items_separator('=')}"
131+
out_str << "\n #{line_items}"
132+
out_str << "\n#{line_items_separator('-')}"
133+
end
134+
end
135+
end
136+
end

0 commit comments

Comments
 (0)