flowcommerce_spree 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6aa92dd937ce7ff2ac430c684e76eaf18f80dc165191b5452407b73a51f6cc59
4
- data.tar.gz: 83270c61b581abf426a1e19d65c63d6c43ea4c766ba3bf67c3fc334cb234e6f6
3
+ metadata.gz: c89d8869da7867a7ae0400dab1005c763c5c98f001336bf915f14743bd279c7a
4
+ data.tar.gz: f664b4f9f7380860b5c5eec7cf5aeda85d59c8b6d2753830f553ccc8734718c4
5
5
  SHA512:
6
- metadata.gz: c45520286feb21871014953ccaa0dd8d18d27520db9b31f05ef062f2e45d3d9b7b4de98807f46d3b4e0ec6acf63ed98e472fe0d37d4290f1b9bcd36ec21363db
7
- data.tar.gz: 37f630c22db5e978c4325ee6a5029cfe5c45940d817b537fd80f3bb750e63fd50d235796d078b0a6ac028136ab3075703b78030edd3a392c35f87e8bd2327740
6
+ metadata.gz: f32478dce9ed19e62ddb9955a0e0d8f7b7388eb9c5079be107282306c85ab524b49f83b35cff0d5835fc79c485d0dd3fc15cb924969ba250dd80182dfb6242c4
7
+ data.tar.gz: b1e68c45f17888c1d717001a2a352957e04a4fb32c47d2864349299d868e01e0cbfd138f7ed599cd5c1a06e3847b4e4745d8630b0472d104c7eceabe6177f518
@@ -4,5 +4,6 @@ module FlowcommerceSpree
4
4
  class Settings < Spree::Preferences::Configuration
5
5
  preference :additional_attributes, :hash, default: {}
6
6
  preference :product_catalog_upload, :hash, default: {}
7
+ preference :notification_setting, :hash, default: {}
7
8
  end
8
9
  end
@@ -11,8 +11,8 @@ module Spree
11
11
  order = item.order
12
12
 
13
13
  if can_calculate_tax?(order)
14
- flow_response = get_flow_tax_data(order)
15
- tax_for_item(item, flow_response)
14
+ get_flow_tax_data(order)
15
+ tax_for_item(item)
16
16
  else
17
17
  prev_tax_amount(item)
18
18
  end
@@ -20,6 +20,13 @@ module Spree
20
20
  alias compute_shipment compute_shipment_or_line_item
21
21
  alias compute_line_item compute_shipment_or_line_item
22
22
 
23
+ def get_tax_rate(taxable)
24
+ order = taxable.class.to_s == 'Spree::Order' ? taxable : taxable.order
25
+ get_flow_tax_data(order) if order.flow_allocations.empty?
26
+ response = order.flow_tax_for_item(taxable.adjustable, 'vat_item_price', rate.included_in_price)
27
+ response.nil? ? 0 : response['rate']&.to_f
28
+ end
29
+
23
30
  private
24
31
 
25
32
  def prev_tax_amount(item)
@@ -39,21 +46,26 @@ module Spree
39
46
 
40
47
  def get_flow_tax_data(order)
41
48
  flow_io_tax_response = Rails.cache.fetch(order.flow_tax_cache_key, time_to_idle: 5.minutes) do
42
- FlowcommerceSpree.client.orders.get_allocations_by_number(FlowcommerceSpree::ORGANIZATION, order.number)
49
+ response = FlowcommerceSpree.client.orders
50
+ .get_allocations_by_number(FlowcommerceSpree::ORGANIZATION, order.number)
51
+ return nil unless response.present?
52
+
53
+ order.flow_order['allocations'] = response.to_hash
54
+ order.update_column(:meta, order.meta.to_json)
55
+ response
43
56
  end
44
57
  flow_io_tax_response
45
58
  end
46
59
 
47
- def tax_for_item(item, flow_response)
60
+ def tax_for_item(item)
61
+ order = item.order
48
62
  prev_tax_amount = prev_tax_amount(item)
49
- return prev_tax_amount if flow_response.nil?
50
-
51
- item_details = flow_response.details&.find do |el|
52
- item.is_a?(Spree::LineItem) ? el.number == item.variant.sku : el.key.value == 'shipping'
53
- end
54
- price_components = rate.included_in_price ? item_details.included : item_details.not_included
63
+ tax_data = order.flow_tax_for_item(item, 'vat_item_price', rate.included_in_price)
64
+ return prev_tax_amount if tax_data.blank?
55
65
 
56
- amount = price_components&.find { |el| el.key.value == 'vat_item_price' }&.total&.amount
66
+ subsidy_data = order.flow_tax_for_item(item, 'vat_subsidy', rate.included_in_price)
67
+ amount = tax_data.dig('total', 'amount')
68
+ amount += subsidy_data.dig('total', 'amount') if subsidy_data.present?
57
69
  amount.present? && amount > 0 ? amount : prev_tax_amount
58
70
  end
59
71
  end
@@ -158,6 +158,24 @@ module Spree
158
158
  address_attributes
159
159
  end
160
160
 
161
+ def flow_allocations
162
+ return @flow_allocations if @flow_allocations
163
+
164
+ @flow_allocations = flow_order&.[]('allocations')
165
+ end
166
+
167
+ def flow_tax_for_item(item, tax_key, included_in_price = true)
168
+ return {} if flow_allocations.blank?
169
+
170
+ item_details = flow_allocations['details']&.find do |el|
171
+ item.is_a?(Spree::LineItem) ? el['number'] == item.variant.sku : el['key'] == 'shipping'
172
+ end
173
+ return {} if item_details.blank?
174
+
175
+ price_components = included_in_price ? item_details['included'] : item_details['not_included']
176
+ price_components&.find { |el| el['key'] == tax_key }
177
+ end
178
+
161
179
  Spree::Order.include(self) if Spree::Order.included_modules.exclude?(self)
162
180
  end
163
181
  end
@@ -7,6 +7,7 @@ module Spree
7
7
  base.serialize :meta, ActiveRecord::Coders::JSON.new(symbolize_keys: true)
8
8
 
9
9
  base.store_accessor :meta, :flow_data, :zone_ids
10
+ base.after_save :sync_variants_with_flow
10
11
  end
11
12
 
12
13
  def price_in_zone(currency, product_zone)
@@ -86,6 +87,10 @@ module Spree
86
87
  prices
87
88
  end
88
89
 
90
+ def sync_variants_with_flow
91
+ variants_including_master.each(&:sync_product_to_flow)
92
+ end
93
+
89
94
  Spree::Product.prepend(self) if Spree::Product.included_modules.exclude?(self)
90
95
  end
91
96
  end
@@ -49,17 +49,23 @@ module Spree
49
49
  product.update_columns(meta: product.meta.to_json)
50
50
  end
51
51
 
52
+ def sync_flow_info?
53
+ if FlowcommerceSpree::API_KEY.blank? || FlowcommerceSpree::API_KEY == 'test_key'
54
+ return { error: 'Api Keys not configured' }
55
+ end
56
+ return { error: 'Price is 0' } if price == 0
57
+ return { error: 'Country of Origin is empty.' } unless country_of_origin
58
+ end
59
+
52
60
  # upload product variant to Flow's Product Catalog
53
61
  def sync_product_to_flow
54
- # initial Spree seed will fail, so skip unless we have Flow data field
55
- return unless respond_to?(:flow_data)
56
-
57
- return if FlowcommerceSpree::API_KEY.blank? || FlowcommerceSpree::API_KEY == 'test_key'
62
+ error = sync_flow_info?
63
+ return error if error.present?
58
64
 
59
- return { error: 'Price is 0' } if price == 0
60
-
61
- return unless country_of_origin
65
+ update_flow_data
66
+ end
62
67
 
68
+ def update_flow_data
63
69
  additional_attrs = {}
64
70
  attr_name = nil
65
71
  export_required = false
@@ -84,7 +90,7 @@ module Spree
84
90
  flow_item_sh1 = Digest::SHA1.hexdigest(flow_item.to_json)
85
91
 
86
92
  # skip if sync not needed
87
- return nil if flow_data&.[](:last_sync_sh1) == flow_item_sh1
93
+ return { error: 'Synchronization not needed' } if flow_data&.[](:last_sync_sh1) == flow_item_sh1
88
94
 
89
95
  response = FlowcommerceSpree.client.items.put_by_number(FlowcommerceSpree::ORGANIZATION, sku, flow_item)
90
96
  self.flow_data ||= {}
@@ -93,6 +99,8 @@ module Spree
93
99
  # after successful put, write cache
94
100
  update_column(:meta, meta.to_json)
95
101
 
102
+ FlowcommerceSpree::ImportItemWorker.perform_async(sku)
103
+
96
104
  response
97
105
  rescue Net::OpenTimeout => e
98
106
  { error: e.message }
@@ -33,14 +33,6 @@ module FlowcommerceSpree
33
33
  item_hash = item.to_hash
34
34
  next unless (variant = Spree::Variant.find_by(sku: item_hash.delete(:number)))
35
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
36
  variant.flow_import_item(item_hash, experience_key: @experience_key)
45
37
 
46
38
  log_str << "#{variant.sku}, "
@@ -60,17 +52,5 @@ module FlowcommerceSpree
60
52
  @organization = organization
61
53
  @zone = zone
62
54
  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
55
  end
76
56
  end
@@ -0,0 +1,45 @@
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 ImportItem
6
+ def self.run(variant, client: FlowcommerceSpree.client, organization: ORGANIZATION)
7
+ new(variant, client: client, organization: organization).run
8
+ end
9
+
10
+ def run
11
+ @client.experiences.get(@organization, status: 'active').each do |experience|
12
+ experience_key = experience.key
13
+ zone = Spree::Zones::Product.find_by(name: experience_key.titleize)
14
+ next unless zone
15
+
16
+ import_data(zone)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def initialize(variant, client:, organization:)
23
+ @client = client
24
+ @logger = client.instance_variable_get(:@http_handler).logger
25
+ @organization = organization
26
+ @variant = variant
27
+ end
28
+
29
+ def import_data(zone)
30
+ experience_key = zone.flow_io_experience
31
+ item = begin
32
+ @client.experiences.get_items_by_number(@organization, @variant.sku, experience: experience_key)
33
+ rescue Io::Flow::V0::HttpClient::PreconditionException, Io::Flow::V0::HttpClient::ServerError => e
34
+ @logger.info "flow.io API error: #{e.message}"
35
+ end
36
+ return unless item
37
+
38
+ item_hash = item.to_hash
39
+
40
+ @variant.flow_import_item(item_hash, experience_key: @experience_key)
41
+
42
+ @logger.info "[#{@variant.sku}][#{experience_key}] Variant experience imported successfully."
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ class ImportItemWorker
5
+ include Sidekiq::Worker
6
+ sidekiq_options retry: 3, queue: :flow_io
7
+
8
+ sidekiq_retries_exhausted do |message, exception|
9
+ Rails.logger.warn("[!] FlowcommerceSpree::ImportItemWorker max attempts reached: #{message} - #{exception}")
10
+ notification_setting = FlowcommerceSpree::Config.notification_setting
11
+ return unless notification_setting[:slack].present?
12
+
13
+ slack_message = "[#{Rails.env}] #{message}"
14
+ Slack_client.chat_postMessage(channel: notification_setting[:slack][:channel], text: slack_message)
15
+ end
16
+
17
+ def perform(variant_sku)
18
+ variant = Spree::Variant.find_by sku: variant_sku
19
+ return unless variant
20
+
21
+ FlowcommerceSpree::ImportItem.run(variant)
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FlowcommerceSpree
4
- VERSION = '0.0.7'
4
+ VERSION = '0.0.8'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flowcommerce_spree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aurel Branzeanu
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-04-16 00:00:00.000000000 Z
12
+ date: 2021-06-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: active_model_serializers
@@ -230,6 +230,7 @@ files:
230
230
  - app/serializers/api/v2/order_serializer_decorator.rb
231
231
  - app/services/flowcommerce_spree/import_experience_items.rb
232
232
  - app/services/flowcommerce_spree/import_experiences.rb
233
+ - app/services/flowcommerce_spree/import_item.rb
233
234
  - app/services/flowcommerce_spree/order_sync.rb
234
235
  - app/services/flowcommerce_spree/order_updater.rb
235
236
  - app/services/flowcommerce_spree/webhooks/capture_upserted_v2.rb
@@ -243,6 +244,7 @@ files:
243
244
  - app/views/spree/admin/promotions/edit.html.erb
244
245
  - app/views/spree/admin/shared/_order_summary.html.erb
245
246
  - app/views/spree/admin/shared/_order_summary_flow.html.erb
247
+ - app/workers/flowcommerce_spree/import_item_worker.rb
246
248
  - config/rails_best_practices.yml
247
249
  - config/routes.rb
248
250
  - db/migrate/20201021160159_add_type_and_meta_to_spree_zone.rb