flowcommerce_spree 0.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/MIT-LICENSE +20 -0
- data/README.md +91 -0
- data/Rakefile +33 -0
- data/SPREE_FLOW.md +134 -0
- data/app/assets/javascripts/flowcommerce_spree/application.js +13 -0
- data/app/assets/stylesheets/flowcommerce_spree/application.css +15 -0
- data/app/controllers/concerns/current_zone_loader_decorator.rb +49 -0
- data/app/controllers/flowcommerce_spree/webhooks_controller.rb +25 -0
- data/app/helpers/flowcommerce_spree/application_helper.rb +6 -0
- data/app/helpers/spree/admin/orders_helper_decorator.rb +17 -0
- data/app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb +53 -0
- data/app/mailers/spree/spree_order_mailer_decorator.rb +24 -0
- data/app/models/flowcommerce_spree/settings.rb +8 -0
- data/app/models/spree/credit_card_decorator.rb +9 -0
- data/app/models/spree/flow_io_product_decorator.rb +91 -0
- data/app/models/spree/flow_io_variant_decorator.rb +205 -0
- data/app/models/spree/gateway/spree_flow_gateway.rb +116 -0
- data/app/models/spree/line_item_decorator.rb +15 -0
- data/app/models/spree/order_decorator.rb +179 -0
- data/app/models/spree/promotion_decorator.rb +10 -0
- data/app/models/spree/promotion_handler/coupon_decorator.rb +30 -0
- data/app/models/spree/spree_user_decorator.rb +15 -0
- data/app/models/spree/taxon_decorator.rb +37 -0
- data/app/models/spree/zone_decorator.rb +7 -0
- data/app/models/spree/zones/flow_io_product_zone_decorator.rb +55 -0
- data/app/services/flowcommerce_spree/import_experience_items.rb +76 -0
- data/app/services/flowcommerce_spree/import_experiences.rb +37 -0
- data/app/services/flowcommerce_spree/order_sync.rb +231 -0
- data/app/views/layouts/flowcommerce_spree/application.html.erb +14 -0
- data/app/views/spree/admin/payments/index.html.erb +28 -0
- data/app/views/spree/admin/promotions/edit.html.erb +57 -0
- data/app/views/spree/admin/shared/_order_summary.html.erb +44 -0
- data/app/views/spree/admin/shared/_order_summary_flow.html.erb +13 -0
- data/app/views/spree/order_mailer/confirm_email.html.erb +86 -0
- data/app/views/spree/order_mailer/confirm_email.text.erb +38 -0
- data/config/initializers/flowcommerce_spree.rb +7 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20201021160159_add_type_and_meta_to_spree_zone.rb +23 -0
- data/db/migrate/20201021755957_add_meta_to_spree_tables.rb +17 -0
- data/db/migrate/20201022173210_add_zone_type_to_spree_zone_members.rb +24 -0
- data/db/migrate/20201022174252_add_kind_to_zone.rb +22 -0
- data/lib/flow/error.rb +73 -0
- data/lib/flow/pay_pal.rb +25 -0
- data/lib/flow/simple_gateway.rb +115 -0
- data/lib/flowcommerce_spree.rb +31 -0
- data/lib/flowcommerce_spree/api.rb +48 -0
- data/lib/flowcommerce_spree/engine.rb +27 -0
- data/lib/flowcommerce_spree/experience_service.rb +65 -0
- data/lib/flowcommerce_spree/logging_http_client.rb +43 -0
- data/lib/flowcommerce_spree/logging_http_handler.rb +15 -0
- data/lib/flowcommerce_spree/refresher.rb +81 -0
- data/lib/flowcommerce_spree/session.rb +71 -0
- data/lib/flowcommerce_spree/version.rb +5 -0
- data/lib/flowcommerce_spree/webhook_service.rb +98 -0
- data/lib/simple_csv_writer.rb +44 -0
- data/lib/tasks/flowcommerce_spree.rake +289 -0
- metadata +220 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Flow (2017)
|
4
|
+
# Enable this modifications if you want to display flow localized line item
|
5
|
+
# Example: https://i.imgur.com/7v2ix2G.png
|
6
|
+
module Spree
|
7
|
+
LineItem.class_eval do
|
8
|
+
# admin show line item price
|
9
|
+
def single_money
|
10
|
+
price = display_price.to_s
|
11
|
+
price += " (#{order.flow_line_item_price(self)})" if order.flow_order
|
12
|
+
price
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# `:display_total` modifications to display total prices beside Spree default. Example: https://i.imgur.com/7v2ix2G.png
|
4
|
+
module Spree # rubocop:disable Metrics/ModuleLength
|
5
|
+
# Added flow specific methods to Spree::Order
|
6
|
+
Order.class_eval do
|
7
|
+
serialize :meta, ActiveRecord::Coders::JSON.new(symbolize_keys: true)
|
8
|
+
|
9
|
+
store_accessor :meta, :flow_data
|
10
|
+
|
11
|
+
before_save :sync_to_flow_io
|
12
|
+
after_touch :sync_to_flow_io
|
13
|
+
|
14
|
+
def sync_to_flow_io
|
15
|
+
return unless zone&.flow_io_active_experience? && state == 'cart' && line_items.size > 0
|
16
|
+
|
17
|
+
flow_io_order = FlowcommerceSpree::OrderSync.new(order: self)
|
18
|
+
flow_io_order.build_flow_request
|
19
|
+
flow_io_order.synchronize! if flow_data['digest'] != flow_io_order.digest
|
20
|
+
end
|
21
|
+
|
22
|
+
def display_total
|
23
|
+
price = FlowcommerceSpree::Api.format_default_price total
|
24
|
+
price += " (#{flow_total})" if flow_order
|
25
|
+
price.html_safe
|
26
|
+
end
|
27
|
+
|
28
|
+
def flow_order
|
29
|
+
return unless flow_data&.[]('order')
|
30
|
+
|
31
|
+
Hashie::Mash.new flow_data['order']
|
32
|
+
end
|
33
|
+
|
34
|
+
# accepts line item, usually called from views
|
35
|
+
def flow_line_item_price(line_item, total = false)
|
36
|
+
result = if flow_order
|
37
|
+
id = line_item.variant.sku
|
38
|
+
|
39
|
+
lines = flow_order.lines || []
|
40
|
+
item = lines.find { |el| el['item_number'] == id }
|
41
|
+
|
42
|
+
return 'n/a' unless item
|
43
|
+
|
44
|
+
total ? item['total']['label'] : item['price']['label']
|
45
|
+
else
|
46
|
+
FlowcommerceSpree::Api.format_default_price(line_item.price * (total ? line_item.quantity : 1))
|
47
|
+
end
|
48
|
+
|
49
|
+
# add line item promo
|
50
|
+
# promo_total, adjustment_total
|
51
|
+
result += " (#{FlowcommerceSpree::Api.format_default_price(line_item.promo_total)})" if line_item.promo_total > 0
|
52
|
+
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
# prepares array of prices that can be easily renderd in templates
|
57
|
+
def flow_cart_breakdown
|
58
|
+
prices = []
|
59
|
+
|
60
|
+
price_model = Struct.new(:name, :label)
|
61
|
+
|
62
|
+
if flow_order
|
63
|
+
# duty, vat, ...
|
64
|
+
unless flow_order.prices
|
65
|
+
message = Flow::Error.format_order_message flow_order
|
66
|
+
raise Flow::Error, message
|
67
|
+
end
|
68
|
+
|
69
|
+
flow_order.prices.each do |price|
|
70
|
+
prices.push price_model.new(price['name'], price['label'])
|
71
|
+
end
|
72
|
+
else
|
73
|
+
price_elements =
|
74
|
+
%i[item_total adjustment_total included_tax_total additional_tax_total tax_total shipment_total promo_total]
|
75
|
+
price_elements.each do |el|
|
76
|
+
price = send(el)
|
77
|
+
if price > 0
|
78
|
+
label = FlowcommerceSpree::Api.format_default_price price
|
79
|
+
prices.push price_model.new(el.to_s.humanize.capitalize, label)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# discount is applied and we allways show it in default currency
|
84
|
+
if adjustment_total != 0
|
85
|
+
formated_discounted_price = FlowcommerceSpree::Api.format_default_price adjustment_total
|
86
|
+
prices.push price_model.new('Discount', formated_discounted_price)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# total
|
91
|
+
prices.push price_model.new(Spree.t(:total), flow_total)
|
92
|
+
|
93
|
+
prices
|
94
|
+
end
|
95
|
+
|
96
|
+
# shows localized total, if possible. if not, fall back to Spree default
|
97
|
+
def flow_total
|
98
|
+
# r flow_order.total.label
|
99
|
+
price = flow_order&.total&.label
|
100
|
+
price || FlowcommerceSpree::Api.format_default_price(total)
|
101
|
+
end
|
102
|
+
|
103
|
+
def flow_experience
|
104
|
+
model = Struct.new(:key)
|
105
|
+
model.new flow_order.experience.key
|
106
|
+
rescue StandardError => _e
|
107
|
+
model.new ENV.fetch('FLOW_BASE_COUNTRY')
|
108
|
+
end
|
109
|
+
|
110
|
+
def flow_io_experience_key
|
111
|
+
flow_data&.[]('exp')
|
112
|
+
end
|
113
|
+
|
114
|
+
def flow_io_experience_from_zone
|
115
|
+
self.flow_data = (flow_data || {}).merge!('exp' => zone.flow_io_experience)
|
116
|
+
end
|
117
|
+
|
118
|
+
def flow_io_order_id
|
119
|
+
flow_data&.dig('order', 'id')
|
120
|
+
end
|
121
|
+
|
122
|
+
def flow_io_attributes
|
123
|
+
flow_data&.dig('order', 'attributes') || {}
|
124
|
+
end
|
125
|
+
|
126
|
+
def add_user_consent_to_flow_data(consent, value)
|
127
|
+
self.flow_data['order'] ||= {}
|
128
|
+
self.flow_data['order']['attributes'] ||= {}
|
129
|
+
self.flow_data['order']['attributes'][consent] = value
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_user_uuid_to_flow_data
|
133
|
+
self.flow_data['order'] ||= {}
|
134
|
+
self.flow_data['order']['attributes'] ||= {}
|
135
|
+
self.flow_data['order']['attributes']['user_uuid'] = user&.uuid
|
136
|
+
end
|
137
|
+
|
138
|
+
def flow_io_user_uuid
|
139
|
+
flow_data&.dig('order', 'attributes', 'user_uuid')
|
140
|
+
end
|
141
|
+
|
142
|
+
def checkout_url
|
143
|
+
"https://checkout.flow.io/#{FlowcommerceSpree::ORGANIZATION}/checkout/#{number}/" \
|
144
|
+
"contact-info?flow_session_id=#{flow_data['session_id']}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# clear invalid zero amount payments. Solidus bug?
|
148
|
+
def clear_zero_amount_payments!
|
149
|
+
# class attribute that can be set to true
|
150
|
+
return unless Flow::Order.clear_zero_amount_payments
|
151
|
+
|
152
|
+
payments.where(amount: 0, state: %w[invalid processing pending]).map(&:destroy)
|
153
|
+
end
|
154
|
+
|
155
|
+
def flow_order_authorized?
|
156
|
+
flow_data&.[]('authorization') ? true : false
|
157
|
+
end
|
158
|
+
|
159
|
+
def flow_order_captured?
|
160
|
+
flow_data['capture'] ? true : false
|
161
|
+
end
|
162
|
+
|
163
|
+
# completes order and sets all states to finalized and complete
|
164
|
+
# used when we have confirmed capture from Flow API or PayPal
|
165
|
+
def flow_finalize!
|
166
|
+
finalize! unless state == 'complete'
|
167
|
+
update_column :payment_state, 'paid' if payment_state != 'paid'
|
168
|
+
update_column :state, 'complete' if state != 'complete'
|
169
|
+
end
|
170
|
+
|
171
|
+
def flow_payment_method
|
172
|
+
if flow_data['payment_type'] == 'paypal'
|
173
|
+
'paypal'
|
174
|
+
else
|
175
|
+
'cc' # creait card is default
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module PromotionHandler
|
5
|
+
Coupon.class_eval do
|
6
|
+
def apply
|
7
|
+
if order.coupon_code.present?
|
8
|
+
if promotion&.actions.exists?
|
9
|
+
experience_key = order.flow_order&.dig('experience', 'key')
|
10
|
+
forbiden_keys = promotion.flow_data&.dig('filter', 'experience') || []
|
11
|
+
|
12
|
+
if experience_key.present? && !forbiden_keys.include?(experience_key)
|
13
|
+
self.error = 'Promotion is not available in current country'
|
14
|
+
else
|
15
|
+
handle_present_promotion(promotion)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
self.error = if Promotion.with_coupon_code(order.coupon_code)&.expired?
|
19
|
+
Spree.t(:coupon_code_expired)
|
20
|
+
else
|
21
|
+
Spree.t(:coupon_code_not_found)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# added flow specific methods to Spree.user_class
|
4
|
+
# which is for Spree in same time
|
5
|
+
# - user object (for admins as well)
|
6
|
+
# - customer object
|
7
|
+
|
8
|
+
Spree.user_class.class_eval do
|
9
|
+
def flow_number
|
10
|
+
return unless id
|
11
|
+
|
12
|
+
token = ENV.fetch('ENCRYPTION_KEY')
|
13
|
+
"su-#{Digest::SHA1.hexdigest(format('%d-%s', id, token))}"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# module Spree
|
4
|
+
# Taxon.class_eval do
|
5
|
+
# def products_by_zone(product_zone)
|
6
|
+
# flow_experience_key = product_zone.flow_data&.[]('key')
|
7
|
+
# sku_regex = product_zone.sku_regex
|
8
|
+
#
|
9
|
+
# if flow_experience_key.present?
|
10
|
+
# products_by_experience(product_zone, sku_regex)
|
11
|
+
# else
|
12
|
+
# products.joins(:master).where('spree_variants.sku ~ ?', sku_regex)
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# def products_by_experience(flow_experience_key, sku_regex)
|
17
|
+
# # To make the following query return a distinct array of products, raw SQL had to be used:
|
18
|
+
# # object.products.joins(:variants).where(
|
19
|
+
# # "spree_variants.meta -> 'flow_data' -> 'exp' ->> '#{flow_experience_key}' IS NOT NULL"
|
20
|
+
# # )
|
21
|
+
# query = <<~SQL
|
22
|
+
# SELECT DISTINCT spree_products.* FROM spree_products
|
23
|
+
# INNER JOIN spree_variants ON spree_variants.product_id = spree_products.id AND
|
24
|
+
# spree_variants.is_master = 'f' AND spree_variants.deleted_at IS NULL AND
|
25
|
+
# (spree_variants.sku ~ '#{sku_regex}')
|
26
|
+
# INNER JOIN (
|
27
|
+
# SELECT spree_products_taxons.*, spree_products_taxons.position as position from spree_products_taxons
|
28
|
+
# ORDER BY position ASC
|
29
|
+
# ) I2 ON spree_products.id = I2.product_id
|
30
|
+
# WHERE spree_products.deleted_at IS NULL AND I2.taxon_id = #{id} AND
|
31
|
+
# (spree_variants.meta -> 'flow_data' -> 'exp' ->> '#{flow_experience_key}' IS NOT NULL)
|
32
|
+
# SQL
|
33
|
+
#
|
34
|
+
# Spree::Product.find_by_sql(query)
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module Zones
|
5
|
+
module FlowIoProductZoneDecorator
|
6
|
+
def self.prepended(base)
|
7
|
+
base.after_update :update_on_flow, if: -> { flow_data&.[]('key').present? }
|
8
|
+
base.before_destroy :remove_on_flow_io, if: -> { flow_data&.[]('key').present? }
|
9
|
+
end
|
10
|
+
|
11
|
+
def available_currencies
|
12
|
+
((currencies || []) + [flow_data&.[]('currency')]).compact.uniq.reject(&:empty?)
|
13
|
+
end
|
14
|
+
|
15
|
+
def flow_io_experience
|
16
|
+
flow_data&.[]('key')
|
17
|
+
end
|
18
|
+
|
19
|
+
def flow_io_experience_currency
|
20
|
+
flow_data&.[]('currency')
|
21
|
+
end
|
22
|
+
|
23
|
+
def flow_io_active_experience?
|
24
|
+
flow_data&.[]('key').present? && flow_data['status'] == 'active'
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_on_flow; end
|
28
|
+
|
29
|
+
def remove_on_flow_io
|
30
|
+
client = FlowcommerceSpree.client
|
31
|
+
client.experiences.delete_by_key(FlowcommerceSpree::ORGANIZATION, flow_data['key'])
|
32
|
+
|
33
|
+
# Flowcommerce `delete_by_key` methods are always returning `nil`, that's why this hack of fetching
|
34
|
+
# @http_handler from client. This handler is a LoggingHttpHandler, which got the http_client attr_reader
|
35
|
+
# implemented specifically for this purpose.
|
36
|
+
false if client.instance_variable_get(:@http_handler).http_client.error
|
37
|
+
end
|
38
|
+
|
39
|
+
def store_flow_io_data(received_experience, logger: FlowcommerceSpree.logger)
|
40
|
+
self.flow_data = received_experience.is_a?(Hash) ? received_experience : received_experience.to_hash
|
41
|
+
self.status = flow_data['status']
|
42
|
+
|
43
|
+
if new_record? && update_attributes(meta: meta, status: status, kind: 'country')
|
44
|
+
logger.info "\nNew flow.io experience imported as product zone: #{name}"
|
45
|
+
elsif update_columns(meta: meta.to_json, status: status, kind: 'country')
|
46
|
+
logger.info "\nProduct zone `#{name}` has been updated from flow.io"
|
47
|
+
end
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
Spree::Zones::Product.prepend(self) if Spree::Zones::Product.included_modules.exclude?(self)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowcommerceSpree
|
4
|
+
# A service object to import the data for product variants belonging to a flow.io Experience
|
5
|
+
class ImportExperienceItems
|
6
|
+
def self.run(zone, client: FlowcommerceSpree.client, organization: ORGANIZATION)
|
7
|
+
new(zone, client: client, organization: organization).run
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
page_size = 100
|
12
|
+
offset = 0
|
13
|
+
items = []
|
14
|
+
total = 0
|
15
|
+
|
16
|
+
while offset == 0 || items.length == 100
|
17
|
+
# show current list size
|
18
|
+
@logger.info "\nGetting items: #{@experience_key.green}, rows #{offset} - #{offset + page_size}"
|
19
|
+
|
20
|
+
begin
|
21
|
+
items = @client.experiences
|
22
|
+
.get_items(@organization, experience: @experience_key, limit: page_size, offset: offset)
|
23
|
+
rescue Io::Flow::V0::HttpClient::PreconditionException => e
|
24
|
+
@logger.info "flow.io API error: #{e.message}"
|
25
|
+
break
|
26
|
+
end
|
27
|
+
|
28
|
+
offset += page_size
|
29
|
+
log_str = +''
|
30
|
+
|
31
|
+
items.each do |item|
|
32
|
+
total += 1
|
33
|
+
item_hash = item.to_hash
|
34
|
+
next unless (variant = Spree::Variant.find_by(sku: item_hash.delete(:number)))
|
35
|
+
|
36
|
+
status_in_experience = item_hash.dig(:local, :status)
|
37
|
+
|
38
|
+
if status_in_experience != 'included'
|
39
|
+
log_str << "[#{status_in_experience.red}]:"
|
40
|
+
else # If at least a variant is included in experience, include the product too
|
41
|
+
adjust_product_zone(variant)
|
42
|
+
end
|
43
|
+
|
44
|
+
variant.flow_import_item(item_hash, experience_key: @experience_key)
|
45
|
+
|
46
|
+
log_str << "#{variant.sku}, "
|
47
|
+
end
|
48
|
+
@logger.info log_str
|
49
|
+
end
|
50
|
+
|
51
|
+
@logger.info "\nData for #{total.to_s.green} products was imported."
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def initialize(zone, client:, organization:)
|
57
|
+
@client = client
|
58
|
+
@experience_key = zone.flow_io_experience
|
59
|
+
@logger = client.instance_variable_get(:@http_handler).logger
|
60
|
+
@organization = organization
|
61
|
+
@zone = zone
|
62
|
+
end
|
63
|
+
|
64
|
+
def adjust_product_zone(variant)
|
65
|
+
return unless (product = variant.product)
|
66
|
+
|
67
|
+
zone_ids = product.zone_ids || []
|
68
|
+
zone_id_string = @zone.id.to_s
|
69
|
+
return if zone_ids.include?(zone_id_string)
|
70
|
+
|
71
|
+
zone_ids << zone_id_string
|
72
|
+
product.zone_ids = zone_ids
|
73
|
+
product.update_columns(meta: product.meta.to_json)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowcommerceSpree
|
4
|
+
# A service object to import the data for of flow.io Experience into Spree::Zones::Product
|
5
|
+
class ImportExperiences
|
6
|
+
def self.run(client: FlowcommerceSpree.client, organization: ORGANIZATION, with_items: nil, refresher: nil)
|
7
|
+
new(client: client, organization: organization, with_items: with_items, refresher: refresher).run
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
# we have to log start, so that another process does not start while this one is running
|
12
|
+
@refresher.log_refresh!
|
13
|
+
|
14
|
+
@client.experiences.get(@organization, status: 'active').each do |experience|
|
15
|
+
experience_key = experience.key
|
16
|
+
zone = Spree::Zones::Product.find_or_initialize_by(name: experience_key.titleize)
|
17
|
+
zone.store_flow_io_data(experience, logger: @refresher.logger)
|
18
|
+
|
19
|
+
next @refresher.logger.info "Error: storing flow.io experience #{experience_key}" if zone.errors.any?
|
20
|
+
|
21
|
+
ImportExperienceItems.run(zone, client: @client) if @with_items
|
22
|
+
end
|
23
|
+
|
24
|
+
# Log sync end time
|
25
|
+
@refresher.log_refresh!(has_ended: true)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def initialize(client:, organization:, with_items: nil, refresher: Refresher.new)
|
31
|
+
@refresher = refresher
|
32
|
+
@client = client
|
33
|
+
@organization = organization
|
34
|
+
@with_items = with_items
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|