-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimporter.rb
113 lines (87 loc) · 3.46 KB
/
importer.rb
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
require "json"
require_relative "receipt"
class Importer
def initialize(filepath)
@filepath = filepath
end
def parse
datetime = receipt_json.dig("transactionDateTime")
receipt = Receipt.new(warehouse:, member:, datetime:)
receipt_json.dig("itemArray").each do |item|
if item_json_is_modifier?(item)
receipt << item_json_to_modifier(item)
else
receipt << item_json_to_item(item)
end
end
verify receipt
receipt
end
private
def warehouse
Warehouse.new(name: receipt_json.dig("warehouseName"), number: receipt_json.dig("warehouseNumber"))
end
def member
Member.new(membership_number: receipt_json.dig("membershipNumber"))
end
def modifier_json(item_json)
associated_item_number = item_json.dig("frenchItemDescription1") # idk why it's stored here
starts_with_slash = associated_item_number&.starts_with? "/"
parsed_associated_item_id = associated_item_number&.match(/\/(\d+)/)&.[](1)&.to_i
return unless starts_with_slash && parsed_associated_item_id.present?
{
associated_item_number: parsed_associated_item_id
}
end
def item_json_is_modifier?(item_json)
# Either discount or additional tax
modifier_json(item_json).present?
end
def item_json_to_item(item_json)
price_cents = (item_json.dig("amount") * 100).round # it should be a whole float
Item.new(
number: item_json.dig("itemNumber"),
name: item_json.dig("itemDescription01"),
price_cents:,
description: item_json.dig("itemDescription02"),
unit: item_json.dig("unit"),
tax_flag: item_json.dig("taxFlag")
)
end
def item_json_to_modifier(item_json)
modifier = modifier_json(item_json)
amount_cents = (item_json.dig("amount") * 100).round # it should be a whole float
Modifier.new(
number: item_json.dig("itemNumber"),
associated_item_number: modifier[:associated_item_number],
unit: item_json.dig("unit").abs,
amount_cents:
)
end
def receipt_json
# Assume there's only one receipt in the array
json.dig("data", "receiptsWithCounts", "receipts")&.first || json.dig("data", "receipts").first
# Costco's GraphQL API used to return receipt data under the `data.receipts`
# key, now it's under `data.receiptsWithCounts.receipts` and contains
# additional keys.
end
def json
@json ||= JSON.parse(file)
end
def file
@file ||= File.read(@filepath)
end
def verify(receipt)
receipt.validate!
# Compare against computed values in receipt JSON
warn "Receipt total mismatch" if receipt.total != receipt_json.dig("total")
warn "Receipt subtotal mismatch" if receipt.subtotal != receipt_json.dig("subTotal")
warn "Receipt tax mismatch" if receipt.tax != receipt_json.dig("taxes")
warn "Receipt total item count mismatch" if receipt.total_items != receipt_json.dig("totalItemCount")
warn "Receipt unique item & modifier count mismatch" if receipt.items.count + receipt.modifiers.count != receipt_json.dig("itemArray").count
warn "Receipt datetime mismatch" if receipt.datetime.strftime('%Y-%m-%dT%H:%M:%S') != receipt_json.dig("transactionDateTime")
warn "Warehouse name mismatch" if receipt.warehouse.name != receipt_json.dig("warehouseName")
warn "Warehouse number mismatch" if receipt.warehouse.number != receipt_json.dig("warehouseNumber")
warn "Membership number mismatch" if receipt.member.membership_number != receipt_json.dig("membershipNumber")
end
end