workarea-listrak 5.0.1
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.
- checksums.yaml +7 -0
- data/.eslintrc.json +36 -0
- data/.esvmrc +5 -0
- data/.gitignore +32 -0
- data/CHANGELOG.md +322 -0
- data/Gemfile +14 -0
- data/README.md +130 -0
- data/Rakefile +53 -0
- data/app/assets/javascripts/workarea/admin/modules/listrak/list_events_select.js +83 -0
- data/app/assets/javascripts/workarea/storefront/listrak/adapters/listrak_adapter.js +110 -0
- data/app/assets/javascripts/workarea/storefront/listrak/listrak_config.js.erb +13 -0
- data/app/assets/javascripts/workarea/storefront/listrak/modules/listrak.js +64 -0
- data/app/controllers/workarea/admin/listrak/configurations_controller.rb +30 -0
- data/app/controllers/workarea/admin/listrak/events_controller.rb +17 -0
- data/app/helpers/workarea/listrak/analytics/helper.rb +44 -0
- data/app/models/workarea/listrak/configuration.rb +19 -0
- data/app/queries/workarea/categorization.decorator +7 -0
- data/app/services/workarea/listrak/data_api/customers.rb +26 -0
- data/app/services/workarea/listrak/data_api/orders.rb +26 -0
- data/app/services/workarea/listrak/data_api/products.rb +26 -0
- data/app/services/workarea/listrak/data_api.rb +63 -0
- data/app/services/workarea/listrak/email_api/contacts.rb +73 -0
- data/app/services/workarea/listrak/email_api/events.rb +44 -0
- data/app/services/workarea/listrak/email_api/lists.rb +21 -0
- data/app/services/workarea/listrak/email_api/messages.rb +50 -0
- data/app/services/workarea/listrak/email_api/transactional_messages.rb +32 -0
- data/app/services/workarea/listrak/email_api.rb +71 -0
- data/app/services/workarea/listrak/models/address.rb +83 -0
- data/app/services/workarea/listrak/models/contact.rb +67 -0
- data/app/services/workarea/listrak/models/contact_form.rb +24 -0
- data/app/services/workarea/listrak/models/customer_form.rb +209 -0
- data/app/services/workarea/listrak/models/event.rb +45 -0
- data/app/services/workarea/listrak/models/event_form.rb +25 -0
- data/app/services/workarea/listrak/models/list.rb +204 -0
- data/app/services/workarea/listrak/models/message_form.rb +147 -0
- data/app/services/workarea/listrak/models/order_form.rb +352 -0
- data/app/services/workarea/listrak/models/order_item.rb +197 -0
- data/app/services/workarea/listrak/models/product_form.rb +369 -0
- data/app/services/workarea/listrak/models/transactional_message_form.rb +21 -0
- data/app/services/workarea/listrak/oauth.rb +45 -0
- data/app/services/workarea/listrak/transactional_message.rb +83 -0
- data/app/view_models/workarea/admin/listrak_configuration_view_model.rb +17 -0
- data/app/views/workarea/admin/listrak/configurations/edit.html.haml +28 -0
- data/app/views/workarea/admin/listrak/events/index.json.jbuilder +4 -0
- data/app/views/workarea/admin/shared/_listrak_configuration_link.html.haml +1 -0
- data/app/views/workarea/storefront/checkouts/_listrak.html.haml +2 -0
- data/app/workers/workarea/listrak/customer_exporter.rb +22 -0
- data/app/workers/workarea/listrak/order_exporter.rb +22 -0
- data/app/workers/workarea/listrak/product_exporter.rb +50 -0
- data/app/workers/workarea/listrak/subscribe_email_signup.rb +34 -0
- data/app/workers/workarea/listrak/unsubscribe_email_signup.rb +27 -0
- data/bin/rails +18 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/rubocop +16 -0
- data/config/initializers/append_points.rb +28 -0
- data/config/initializers/configuration.rb +8 -0
- data/config/locales/en.yml +17 -0
- data/config/routes.rb +10 -0
- data/docs/Listrak Technical Integration Guide.docx +0 -0
- data/lib/generators/workarea/listrak/message/USAGE +9 -0
- data/lib/generators/workarea/listrak/message/message_generator.rb +15 -0
- data/lib/generators/workarea/listrak/message/templates/message.rb.erb +24 -0
- data/lib/workarea/listrak/analytics.rb +17 -0
- data/lib/workarea/listrak/bogus_data_api/customers.rb +9 -0
- data/lib/workarea/listrak/bogus_data_api/orders.rb +9 -0
- data/lib/workarea/listrak/bogus_data_api/products.rb +9 -0
- data/lib/workarea/listrak/bogus_data_api/proxy_client.rb +29 -0
- data/lib/workarea/listrak/bogus_data_api.rb +46 -0
- data/lib/workarea/listrak/bogus_email_api/contacts.rb +8 -0
- data/lib/workarea/listrak/bogus_email_api/events.rb +23 -0
- data/lib/workarea/listrak/bogus_email_api/lists.rb +63 -0
- data/lib/workarea/listrak/bogus_email_api/proxy_client.rb +29 -0
- data/lib/workarea/listrak/bogus_email_api/transactional_messages.rb +9 -0
- data/lib/workarea/listrak/bogus_email_api.rb +51 -0
- data/lib/workarea/listrak/engine.rb +12 -0
- data/lib/workarea/listrak/error.rb +39 -0
- data/lib/workarea/listrak/version.rb +5 -0
- data/lib/workarea/listrak.rb +57 -0
- data/readme/listrak-admin.png +0 -0
- data/script/admin_ci +9 -0
- data/script/ci +11 -0
- data/script/core_ci +9 -0
- data/script/plugins_ci +9 -0
- data/script/storefront_ci +9 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +34 -0
- data/test/dummy/bin/update +29 -0
- data/test/dummy/config/application.rb +18 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +9 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +54 -0
- data/test/dummy/config/environments/production.rb +86 -0
- data/test/dummy/config/environments/test.rb +43 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/workarea.rb +5 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/puma.rb +47 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/secrets.yml +38 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/db/seeds.rb +2 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/factories/workarea/factories/listrak.rb +15 -0
- data/test/helpers/workarea/listrak/analytics/helper_test.rb +38 -0
- data/test/integration/workarea/admin/listrak/configuration_integration_test.rb +26 -0
- data/test/integration/workarea/admin/listrak/events_integration_test.rb +21 -0
- data/test/lib/generators/workarea/listrak/message_generator_test.rb +26 -0
- data/test/queries/workarea/categorization_test.decorator +15 -0
- data/test/services/workarea/listrak/data_api/customers_test.rb +35 -0
- data/test/services/workarea/listrak/data_api/orders_test.rb +24 -0
- data/test/services/workarea/listrak/data_api/products_test.rb +27 -0
- data/test/services/workarea/listrak/email_api/contacts_test.rb +55 -0
- data/test/services/workarea/listrak/email_api/events_test.rb +51 -0
- data/test/services/workarea/listrak/email_api/lists_test.rb +45 -0
- data/test/services/workarea/listrak/email_api/messages_test.rb +28 -0
- data/test/services/workarea/listrak/email_api/transactional_messages_test.rb +50 -0
- data/test/services/workarea/listrak/oauth_test.rb +27 -0
- data/test/services/workarea/listrak/transactional_message_test.rb +48 -0
- data/test/support/workarea/listrak/test_order_confirmation_email.rb +116 -0
- data/test/support/workarea/listrak/vcr_config.rb +31 -0
- data/test/system/workarea/listrak_system_test.rb +75 -0
- data/test/test_helper.rb +16 -0
- data/test/vcr_cassettes/listrak/data_api/customers_import-successful.yml +88 -0
- data/test/vcr_cassettes/listrak/data_api/orders_import-successful.yml +90 -0
- data/test/vcr_cassettes/listrak/data_api/products_import-successful.yml +88 -0
- data/test/vcr_cassettes/listrak/email_api/contacts_get-successful.yml +87 -0
- data/test/vcr_cassettes/listrak/email_api/contacts_upsert-already_unsubscribed.yml +90 -0
- data/test/vcr_cassettes/listrak/email_api/contacts_upsert-successful.yml +87 -0
- data/test/vcr_cassettes/listrak/email_api/events_create-invalid_parameter.yml +90 -0
- data/test/vcr_cassettes/listrak/email_api/events_create-successful.yml +87 -0
- data/test/vcr_cassettes/listrak/email_api/events_get-successful.yml +88 -0
- data/test/vcr_cassettes/listrak/email_api/lists_all-successful.yml +88 -0
- data/test/vcr_cassettes/listrak/email_api/messages_create-successful.yml +88 -0
- data/test/vcr_cassettes/listrak/email_api/transactional_messages_create-successful.yml +89 -0
- data/test/vcr_cassettes/listrak/oauth-successful.yml +44 -0
- data/test/vcr_cassettes/listrak/oauth-unsuccessful.yml +46 -0
- data/test/vcr_cassettes/listrak/system_tests/footer_email_signup-successful.yml +171 -0
- data/test/vcr_cassettes/listrak/system_tests/footer_email_signup_overrides_unsubscribe.yml +255 -0
- data/test/vcr_cassettes/listrak/system_tests/unsubscribing.yml +593 -0
- data/test/workers/workarea/listrak/order_exporter_test.rb +31 -0
- data/test/workers/workarea/listrak/product_exporter_test.rb +62 -0
- data/workarea-listrak.gemspec +16 -0
- metadata +223 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Listrak
|
|
3
|
+
module Models
|
|
4
|
+
class OrderItem
|
|
5
|
+
|
|
6
|
+
attr_reader :order, :order_item
|
|
7
|
+
|
|
8
|
+
def initialize(order, order_item)
|
|
9
|
+
@order = order
|
|
10
|
+
@order_item = order_item
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def as_json
|
|
14
|
+
{
|
|
15
|
+
discountDescription: discount_description,
|
|
16
|
+
discountType: discount_type,
|
|
17
|
+
discountedPrice: discount_price,
|
|
18
|
+
itemTotal: item_total,
|
|
19
|
+
itemDiscountTotal: item_discount_total,
|
|
20
|
+
meta1: meta1,
|
|
21
|
+
meta2: meta2,
|
|
22
|
+
meta3: meta3,
|
|
23
|
+
meta4: meta4,
|
|
24
|
+
meta5: meta5,
|
|
25
|
+
orderNumber: order_number,
|
|
26
|
+
price: price,
|
|
27
|
+
quantity: quantity,
|
|
28
|
+
shipDate: ship_date,
|
|
29
|
+
shippingMethod: shipping_method,
|
|
30
|
+
sku: sku,
|
|
31
|
+
status: status,
|
|
32
|
+
trackingNumber: tracking_number
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Description of the discount for the line item
|
|
37
|
+
#
|
|
38
|
+
# @return [String]
|
|
39
|
+
#
|
|
40
|
+
def discount_description
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Type of discount for the line item
|
|
44
|
+
#
|
|
45
|
+
# "NotSet" "PriceOverride" "PriceRule" "Promotion" "SeniorCitizen" "Markdown" "Coupon" "QuantityDiscount" "Rebate" "CashDiscount" "TradeDiscount" "TradeInKind" "PromptPaymentDiscount" "GeneralDiscount" "GiftVoucher" "FlexibleDiscount" "RewardProgram" "ManufacturerReward" "CreditCardReward"
|
|
46
|
+
#
|
|
47
|
+
# @return [String]
|
|
48
|
+
#
|
|
49
|
+
def discount_type
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Total discounted cost of product
|
|
53
|
+
#
|
|
54
|
+
# @return [Float]
|
|
55
|
+
#
|
|
56
|
+
def discount_price
|
|
57
|
+
order_item.price_adjustments
|
|
58
|
+
.select { |pa| pa.data['discount_value'].present? && pa.price == 'item' }
|
|
59
|
+
.sum { |d| d.data['discount_value'] }
|
|
60
|
+
.abs
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Total line item cost (quantity times price)
|
|
64
|
+
#
|
|
65
|
+
# @return [Float]
|
|
66
|
+
#
|
|
67
|
+
def item_total
|
|
68
|
+
order_item.total_value.to_f
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Total amount of the discount for the line item
|
|
72
|
+
#
|
|
73
|
+
# @return [Float]
|
|
74
|
+
#
|
|
75
|
+
def item_discount_total
|
|
76
|
+
order_item.price_adjustments
|
|
77
|
+
.select { |pa| pa.data['discount_value'].present? && pa.price == 'item' }
|
|
78
|
+
.sum { |d| d.data['discount_value'] }
|
|
79
|
+
.abs
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Additional Optional Information
|
|
83
|
+
#
|
|
84
|
+
# limited to 500 characters
|
|
85
|
+
#
|
|
86
|
+
# @return [String]
|
|
87
|
+
#
|
|
88
|
+
def meta1
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Additional Optional Information
|
|
92
|
+
#
|
|
93
|
+
# limited to 500 characters
|
|
94
|
+
#
|
|
95
|
+
# @return [String]
|
|
96
|
+
#
|
|
97
|
+
def meta2
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Additional Optional Information
|
|
101
|
+
#
|
|
102
|
+
# limited to 500 characters
|
|
103
|
+
#
|
|
104
|
+
# @return [String]
|
|
105
|
+
#
|
|
106
|
+
def meta3
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Additional Optional Information
|
|
110
|
+
#
|
|
111
|
+
# limited to 500 characters
|
|
112
|
+
#
|
|
113
|
+
# @return [String]
|
|
114
|
+
#
|
|
115
|
+
def meta4
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Additional Optional Information
|
|
119
|
+
#
|
|
120
|
+
# limited to 500 characters
|
|
121
|
+
#
|
|
122
|
+
# @return [String]
|
|
123
|
+
#
|
|
124
|
+
def meta5
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Order number
|
|
128
|
+
#
|
|
129
|
+
# limited to 50 characters
|
|
130
|
+
#
|
|
131
|
+
# @return [String]
|
|
132
|
+
#
|
|
133
|
+
def order_number
|
|
134
|
+
order.id.to_s
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Price of one unit purchased
|
|
138
|
+
#
|
|
139
|
+
# @return [Float]
|
|
140
|
+
#
|
|
141
|
+
def price
|
|
142
|
+
order_item.original_unit_price.to_f
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Total number of units purchased
|
|
146
|
+
#
|
|
147
|
+
# return [Integer]
|
|
148
|
+
#
|
|
149
|
+
def quantity
|
|
150
|
+
order_item.quantity
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Timestamp when item shipped (ET)
|
|
154
|
+
#
|
|
155
|
+
# @return [String] DateTime in %FT%TZ
|
|
156
|
+
#
|
|
157
|
+
def ship_date
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Shipping method (e.g. UPS Ground)
|
|
161
|
+
#
|
|
162
|
+
# limited to 32 characters
|
|
163
|
+
#
|
|
164
|
+
# @return [String]
|
|
165
|
+
#
|
|
166
|
+
def shipping_method
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Unique stock number of product
|
|
170
|
+
#
|
|
171
|
+
# limited to 100 characters
|
|
172
|
+
#
|
|
173
|
+
# @return [String]
|
|
174
|
+
#
|
|
175
|
+
def sku
|
|
176
|
+
order_item.sku
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Status indicator
|
|
180
|
+
#
|
|
181
|
+
# "NotSet" "Misc" "PreOrder" "BackOrder" "Pending" "Hold" "Processing" "Shipped" "Completed" "Returned" "Canceled" "Unknown" "Closed"
|
|
182
|
+
#
|
|
183
|
+
# @return [String]
|
|
184
|
+
#
|
|
185
|
+
def status
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Shipment tracking number
|
|
189
|
+
#
|
|
190
|
+
# limited to 32 characters
|
|
191
|
+
#
|
|
192
|
+
def tracking_number
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Listrak
|
|
3
|
+
module Models
|
|
4
|
+
class ProductForm
|
|
5
|
+
include ActionView::Helpers::AssetUrlHelper
|
|
6
|
+
include Workarea::I18n::DefaultUrlOptions
|
|
7
|
+
include Workarea::ApplicationHelper
|
|
8
|
+
include Storefront::Engine.routes.url_helpers
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# @param [Workarea::Catalog::Product] product
|
|
12
|
+
# @param [String] sku
|
|
13
|
+
def initialize(product, sku, pricing_sku: nil, inventory_sku: nil)
|
|
14
|
+
@product = product
|
|
15
|
+
@sku = sku
|
|
16
|
+
@pricing_sku = pricing_sku
|
|
17
|
+
@inventory_sku = inventory_sku
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def as_json(_options)
|
|
21
|
+
{
|
|
22
|
+
brand: brand,
|
|
23
|
+
category: category,
|
|
24
|
+
color: color,
|
|
25
|
+
description: description,
|
|
26
|
+
discontinued: discontinued,
|
|
27
|
+
gender: gender,
|
|
28
|
+
imageUrl: image_url,
|
|
29
|
+
inStock: in_stock,
|
|
30
|
+
isClearance: is_clearence,
|
|
31
|
+
isOutlet: is_outlet,
|
|
32
|
+
isPurchasable: is_purchasable,
|
|
33
|
+
isViewable: is_viewable,
|
|
34
|
+
linkUrl: link_url,
|
|
35
|
+
masterSku: master_sku,
|
|
36
|
+
meta1: meta1,
|
|
37
|
+
meta2: meta2,
|
|
38
|
+
meta3: meta3,
|
|
39
|
+
meta4: meta4,
|
|
40
|
+
meta5: meta5,
|
|
41
|
+
msrp: msrp,
|
|
42
|
+
onSale: on_sale,
|
|
43
|
+
price: price,
|
|
44
|
+
qoh: quantity_on_hand,
|
|
45
|
+
saleEndDate: sale_end_date,
|
|
46
|
+
salePrice: sale_price,
|
|
47
|
+
saleStartDate: sale_start_date,
|
|
48
|
+
size: size,
|
|
49
|
+
sku: sku,
|
|
50
|
+
style: style,
|
|
51
|
+
subCategory: sub_category,
|
|
52
|
+
title: title,
|
|
53
|
+
unitCost: unit_cost
|
|
54
|
+
}.compact
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Brand name of product
|
|
58
|
+
#
|
|
59
|
+
# limited to 50 characters
|
|
60
|
+
#
|
|
61
|
+
# @return [String]
|
|
62
|
+
#
|
|
63
|
+
def brand
|
|
64
|
+
product.try(:brand)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Category or department
|
|
68
|
+
#
|
|
69
|
+
# limited to 50 characters
|
|
70
|
+
#
|
|
71
|
+
# @return [String]
|
|
72
|
+
#
|
|
73
|
+
def category
|
|
74
|
+
categorization.default_model.try(:name)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Color of product (e.g. green, paisley, etc.)
|
|
78
|
+
#
|
|
79
|
+
# limited to 100 characters
|
|
80
|
+
#
|
|
81
|
+
# @return [String]
|
|
82
|
+
#
|
|
83
|
+
def color
|
|
84
|
+
variant.fetch_detail('color')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Description of product
|
|
88
|
+
#
|
|
89
|
+
# limited to 4000 characters
|
|
90
|
+
#
|
|
91
|
+
# @return [String]
|
|
92
|
+
#
|
|
93
|
+
def description
|
|
94
|
+
product.description
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Explicit indicator that the item has been discontinued
|
|
98
|
+
#
|
|
99
|
+
# @return [Boolean]
|
|
100
|
+
#
|
|
101
|
+
def discontinued
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Gender of product (if applicable)
|
|
105
|
+
#
|
|
106
|
+
# limited to 50 characters
|
|
107
|
+
#
|
|
108
|
+
# @return [String]
|
|
109
|
+
#
|
|
110
|
+
def gender
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# URL for product image
|
|
114
|
+
#
|
|
115
|
+
# limited to 500 characters
|
|
116
|
+
#
|
|
117
|
+
# @return [String]
|
|
118
|
+
#
|
|
119
|
+
def image_url
|
|
120
|
+
return unless image = variant_image || product.images.first
|
|
121
|
+
@image_url ||= product_image_url(image, :detail)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Explicit indicator that the item is in stock
|
|
125
|
+
#
|
|
126
|
+
# @return [Boolean]
|
|
127
|
+
#
|
|
128
|
+
def in_stock
|
|
129
|
+
quantity_on_hand > 0
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Explicit indicator that the item is a clearance item
|
|
133
|
+
#
|
|
134
|
+
# @return [Boolean]
|
|
135
|
+
#
|
|
136
|
+
def is_clearence
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Explicit indicator that the item is an outlet item
|
|
140
|
+
#
|
|
141
|
+
# @return [Boolean]
|
|
142
|
+
#
|
|
143
|
+
def is_outlet
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Explicit indicator that the item can be included in recommendations
|
|
147
|
+
#
|
|
148
|
+
# @return [Boolean]
|
|
149
|
+
#
|
|
150
|
+
def is_purchasable
|
|
151
|
+
product.purchasable? && inventory_sku.purchasable?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Flag used in some systems to determine if the item should be included in recommendations
|
|
155
|
+
#
|
|
156
|
+
# @return [Boolean]
|
|
157
|
+
#
|
|
158
|
+
def is_viewable
|
|
159
|
+
product.purchasable? && inventory_sku.purchasable?
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# URL for product webpage
|
|
163
|
+
#
|
|
164
|
+
# limited to 500 characters
|
|
165
|
+
#
|
|
166
|
+
# @return [String]
|
|
167
|
+
#
|
|
168
|
+
def link_url
|
|
169
|
+
product_url(product, host: Workarea.config.host)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Unique stock number of the master product
|
|
173
|
+
#
|
|
174
|
+
# limited to 100 characters
|
|
175
|
+
#
|
|
176
|
+
# @return [String]
|
|
177
|
+
#
|
|
178
|
+
def master_sku
|
|
179
|
+
product.id.to_s
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Additional meta imformation
|
|
183
|
+
#
|
|
184
|
+
# limted to 500 characters
|
|
185
|
+
#
|
|
186
|
+
# @return [String]
|
|
187
|
+
#
|
|
188
|
+
def meta1
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Additional meta imformation
|
|
192
|
+
#
|
|
193
|
+
# limted to 500 characters
|
|
194
|
+
#
|
|
195
|
+
# @return [String]
|
|
196
|
+
#
|
|
197
|
+
def meta2
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Additional meta imformation
|
|
201
|
+
#
|
|
202
|
+
# limted to 500 characters
|
|
203
|
+
#
|
|
204
|
+
# @return [String]
|
|
205
|
+
#
|
|
206
|
+
def meta3
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Additional meta imformation
|
|
210
|
+
#
|
|
211
|
+
# limted to 500 characters
|
|
212
|
+
#
|
|
213
|
+
# @return [String]
|
|
214
|
+
#
|
|
215
|
+
def meta4
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Additional meta imformation
|
|
219
|
+
#
|
|
220
|
+
# limted to 500 characters
|
|
221
|
+
#
|
|
222
|
+
# @return [String]
|
|
223
|
+
#
|
|
224
|
+
def meta5
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Retail price of the product
|
|
228
|
+
#
|
|
229
|
+
# @return [Float]
|
|
230
|
+
#
|
|
231
|
+
def msrp
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Explicit indicator that the item is on sale
|
|
235
|
+
#
|
|
236
|
+
# @return [Boolean
|
|
237
|
+
#
|
|
238
|
+
def on_sale
|
|
239
|
+
pricing_sku.on_sale?
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# List price of product
|
|
243
|
+
#
|
|
244
|
+
# @return [Float]
|
|
245
|
+
#
|
|
246
|
+
def price
|
|
247
|
+
pricing_sku.find_price.sell.to_f
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Quantity on hand
|
|
251
|
+
#
|
|
252
|
+
# return [Integer]
|
|
253
|
+
#
|
|
254
|
+
def quantity_on_hand
|
|
255
|
+
inventory_sku.available
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# End date time of sale
|
|
259
|
+
#
|
|
260
|
+
# @return [String] sale_end_date DateTime in %FT%TZ
|
|
261
|
+
#
|
|
262
|
+
def sale_end_date
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Sale price of product
|
|
266
|
+
#
|
|
267
|
+
# @return [Float
|
|
268
|
+
#
|
|
269
|
+
def sale_price
|
|
270
|
+
pricing_sku.find_price.sale.to_f
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Start date time of sale
|
|
274
|
+
#
|
|
275
|
+
# @return [String] sale_start_date DateTime in %FT%TZ
|
|
276
|
+
#
|
|
277
|
+
def sale_start_date
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Size of product (e.g. small, M, 6 7/8”)
|
|
281
|
+
#
|
|
282
|
+
# limited to 50 characters
|
|
283
|
+
#
|
|
284
|
+
# @return [String]
|
|
285
|
+
#
|
|
286
|
+
def size
|
|
287
|
+
variant.fetch_detail('size')
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Unique stock number of product
|
|
291
|
+
#
|
|
292
|
+
# limited to 100 characters
|
|
293
|
+
#
|
|
294
|
+
# @return [String]
|
|
295
|
+
#
|
|
296
|
+
def sku
|
|
297
|
+
@sku
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Style of product
|
|
301
|
+
#
|
|
302
|
+
# limited to 100 characters
|
|
303
|
+
#
|
|
304
|
+
# @return [String]
|
|
305
|
+
#
|
|
306
|
+
def style
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Sub-category or sub-department
|
|
310
|
+
#
|
|
311
|
+
# limited to 50 characters
|
|
312
|
+
#
|
|
313
|
+
# @return [String]
|
|
314
|
+
#
|
|
315
|
+
def sub_category
|
|
316
|
+
categorization.secondary_model.try(:name)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Product name
|
|
320
|
+
#
|
|
321
|
+
# limited to 500 characters
|
|
322
|
+
#
|
|
323
|
+
# @return [String]
|
|
324
|
+
#
|
|
325
|
+
def title
|
|
326
|
+
product.name
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Price per individual item
|
|
330
|
+
#
|
|
331
|
+
# @return [Float]
|
|
332
|
+
#
|
|
333
|
+
def unit_cost
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
private
|
|
337
|
+
|
|
338
|
+
attr_reader :product
|
|
339
|
+
|
|
340
|
+
def categorization
|
|
341
|
+
@categorization ||= Categorization.new(product)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def variant
|
|
345
|
+
@variant ||= product.variants.find_by(sku: sku)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def pricing_sku
|
|
349
|
+
@pricing_sku ||= Pricing::Sku.find sku
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def inventory_sku
|
|
353
|
+
@inventory_sku ||= Inventory::Sku.find sku
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def variant_image
|
|
357
|
+
@variant_image ||=
|
|
358
|
+
begin
|
|
359
|
+
sku_options = variant.details.values.flat_map { |options| options.map(&:optionize) }
|
|
360
|
+
|
|
361
|
+
product.images.detect do |image|
|
|
362
|
+
sku_options.include?(image.option.optionize)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Listrak
|
|
3
|
+
module Models
|
|
4
|
+
class TransactionalMessageForm
|
|
5
|
+
attr_reader :email_address, :segmentation_field_values
|
|
6
|
+
|
|
7
|
+
def initialize(email_address:, segmentation_field_values: [])
|
|
8
|
+
@email_address = email_address
|
|
9
|
+
@segmentation_field_values = segmentation_field_values
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_json
|
|
13
|
+
{
|
|
14
|
+
emailAddress: email_address,
|
|
15
|
+
segmentationFieldValues: segmentation_field_values
|
|
16
|
+
}.compact.to_json
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Listrak
|
|
3
|
+
module Oauth
|
|
4
|
+
# Generates an oauth token for a client_id and client_secret.
|
|
5
|
+
#
|
|
6
|
+
# @param client_id [String] client id
|
|
7
|
+
# @param client_secret [String] client secret
|
|
8
|
+
# @param options [Hash] extra options when getting the OAuth token
|
|
9
|
+
# @option options [Integer] timeout value for open and read timeouts
|
|
10
|
+
# @option options [Integer] open_timeout value for open timeout
|
|
11
|
+
# @option options [Integer] read_timeout value for read timeout
|
|
12
|
+
#
|
|
13
|
+
# @raise [OauthError] raised if generating an Oauth token is unsucessful
|
|
14
|
+
#
|
|
15
|
+
# @return [String] Oauth token
|
|
16
|
+
def self.token(client_id:, client_secret:, **options)
|
|
17
|
+
cache_key = "listrak/api/#{client_id}"
|
|
18
|
+
token = Rails.cache.read(cache_key)
|
|
19
|
+
|
|
20
|
+
return token if token.present?
|
|
21
|
+
|
|
22
|
+
uri = URI('https://auth.listrak.com/OAuth2/Token')
|
|
23
|
+
params = {
|
|
24
|
+
grant_type: 'client_credentials',
|
|
25
|
+
client_id: client_id,
|
|
26
|
+
client_secret: client_secret
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
response = Net::HTTP.post_form uri, params
|
|
30
|
+
|
|
31
|
+
case response
|
|
32
|
+
when ::Net::HTTPSuccess
|
|
33
|
+
body = JSON.parse response.body
|
|
34
|
+
token = body["access_token"]
|
|
35
|
+
expiry = body["expires_in"] - 60
|
|
36
|
+
|
|
37
|
+
Rails.cache.write(cache_key, token, expires_in: expiry)
|
|
38
|
+
token
|
|
39
|
+
else
|
|
40
|
+
raise OauthError.new response
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Listrak
|
|
3
|
+
# Base functionality for Listrak message sender classes.
|
|
4
|
+
# +Listrak::TransactionalMessage+ objects wrap a call to the
|
|
5
|
+
# Listrak REST API and allow the user to cleanly define
|
|
6
|
+
# attribute ID / value pairs and "name" them by defining
|
|
7
|
+
# attribute values as method calls.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# module Workarea
|
|
11
|
+
# class OrderConfirmationMessage
|
|
12
|
+
# include Listrak::TransactionalMessage
|
|
13
|
+
#
|
|
14
|
+
# message_id 12345
|
|
15
|
+
# message_attributes name: 67890
|
|
16
|
+
#
|
|
17
|
+
# def initialize(model)
|
|
18
|
+
# @model = model
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# def name
|
|
22
|
+
# @model.class.name
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# private
|
|
26
|
+
#
|
|
27
|
+
# def email_address
|
|
28
|
+
# model.email_address
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
module TransactionalMessage
|
|
33
|
+
extend ActiveSupport::Concern
|
|
34
|
+
|
|
35
|
+
included do
|
|
36
|
+
class_attribute :listrak_message_id
|
|
37
|
+
class_attribute :transactional_attributes
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class_methods do
|
|
41
|
+
def message_id(id)
|
|
42
|
+
self.listrak_message_id = id
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def message_attributes(attributes)
|
|
46
|
+
self.transactional_attributes = attributes.symbolize_keys
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @abstract Subclass is expected to implement #email_address
|
|
51
|
+
# @!method email_address
|
|
52
|
+
# Email address to deliver this message to
|
|
53
|
+
|
|
54
|
+
# Sends the transactional mesasge
|
|
55
|
+
#
|
|
56
|
+
# @return [nil]
|
|
57
|
+
#
|
|
58
|
+
def deliver
|
|
59
|
+
transactional_message_form = Listrak::Models::TransactionalMessageForm.new(
|
|
60
|
+
email_address: email_address,
|
|
61
|
+
segmentation_field_values: segmentation_field_values
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
Listrak.email.transactional_messages.create(
|
|
65
|
+
Listrak.configuration.default_list_id,
|
|
66
|
+
listrak_message_id,
|
|
67
|
+
transactional_message_form
|
|
68
|
+
)
|
|
69
|
+
nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# @return [Array<Hash>]
|
|
75
|
+
#
|
|
76
|
+
def segmentation_field_values
|
|
77
|
+
transactional_attributes.map do |name, id|
|
|
78
|
+
{ segmentationFieldId: id, value: send(name).to_s }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|