Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DL-82] Import Flow HS code #32

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions app/services/flowcommerce_spree/import_items_hs_codes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

module FlowcommerceSpree
# A service object to import the data for product variants belonging to a flow.io Experience
class ImportItemsHsCodes
texpert marked this conversation as resolved.
Show resolved Hide resolved
def self.run(client: FlowcommerceSpree.client, organization: ORGANIZATION)
new(client: client, organization: organization).run
end

def run # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
page_size = 100
offset = 0
items = []
updated = []
with_errors = []
total = 0

while offset == 0 || items.length != 0
# show current list size
@logger.info "\nGetting items: rows #{offset} - #{offset + page_size}"

begin
items = @client.hs10.get(@organization, limit: page_size, offset: offset)
rescue Io::Flow::V0::HttpClient::PreconditionException => e
@logger.info "flow.io API error: #{e.message}"
break
end

offset += page_size
log_str = +''

items.group_by { |hs_code| hs_code.item.number }.each do |item|
total += 1
begin
variant_sku = item.first
next unless (variant = Spree::Variant.find_by(sku: variant_sku))

hs_code_data_list = item.last
hs_code = hs_code_data_list&.first&.code&.[](0..5)
variant.flow_data ||= {}
variant.flow_data['hs_code'] = hs_code
variant.update_column(:meta, variant.meta.to_json)
log_str << "#{variant.sku}, "
updated << variant.sku
rescue StandardError
with_errors = variant.sku
end
end
@logger.info log_str
end

VariantService.new.update_flow_classification(updated)

@logger.info "\nData for #{total.to_s.green} products was imported."
end

private

def initialize(client:, organization:)
@client = client
@logger = client.instance_variable_get(:@http_handler).logger
@organization = organization
end
end
end
48 changes: 48 additions & 0 deletions lib/tasks/spree_variant.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

namespace :spree_variant do
desc 'Import Flow Hs Codes from CSV'
task import_flow_hs_code_from_csv: :environment do
s3_file_path = CSVUploader.download_url('script/flow_hs_codes.csv', 'flow_hs_code')
csv = CSV.new(URI.parse(s3_file_path).open, headers: true, col_sep: ',')

not_found = []
updated = []
with_errors = []

csv.each do |row|
begin
hs_code = row['hs6']
next unless hs_code.present?

sku = row['item_number']
next not_found << sku unless (variant = Spree::Variant.find_by(sku: row['item_number']))

variant.flow_data ||= {}
variant.flow_data['hs_code'] = hs_code
variant.update_column(:meta, variant.meta.to_json)
updated << sku
rescue StandardError
with_errors << sku
end
end

VariantService.new.update_flow_classification(updated)

puts "\n#{Time.zone.now} | Not found in the DB #{not_found.size}."
puts not_found.inspect

puts "\n#{Time.zone.now} | Unexpected errors while updating #{with_errors.size}."
puts with_errors.inspect

puts "\n#{Time.zone.now} | Updated #{updated.size}."
puts "Updated #{updated} variants."

puts '[Important] Review task output to see if all products where properly synchronized with Fulfil.'
end

desc 'Import Flow Hs Codes from Api'
task import_flow_hs_code: :environment do
FlowcommerceSpree::ImportItemsHsCodes.run
end
end
11 changes: 11 additions & 0 deletions spec/dummy/app/models/concerns/csv_uploader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class CSVUploader
def initialize(file)
timestamp = Time.current.strftime('%Y%m%d%H%M%S')
@filename = "#{timestamp}_#{file.original_filename}"
@file_content = CSV.parse(file.read)
end

def self.download_url(file_path, filename); end
end
11 changes: 11 additions & 0 deletions spec/dummy/app/services/variant_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class VariantService
attr_accessor :variant

def initialize(variant = nil)
@variant = variant
end

def update_flow_classification(variant_skus = []); end
end
18 changes: 18 additions & 0 deletions spec/factories/flow_io/hs_code.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

FactoryBot.define do
factory :flow_hs_code, class: Io::Flow::V0::Models::Hs10 do
id { Faker::Guid.guid }
origin { 'CAN' }
destination { 'DEU' }
code { '71131910' }
item do
Io::Flow::V0::Models::HarmonizedItemReference.new(
description: 'jewellery precious_metal',
id: 'cit-b1d68224735e4828a51b0be90ad37c6f',
number: Spree::Variant.first&.sku || create(:base_variant).sku
)
end
initialize_with { new(**attributes) }
end
end
1 change: 1 addition & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require 'support/database_cleaner.rb'
require 'support/flow.rb'
require 'support/controller_requests.rb'
require 'support/tasks.rb'

# Add additional requires below this line. Rails is not loaded until this point!

Expand Down
29 changes: 29 additions & 0 deletions spec/service/import_items_hs_codes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require 'rails_helper'
require 'colorize'

module FlowcommerceSpree
RSpec.describe ImportItemsHsCodes do
let(:hs_code_data) { build(:flow_hs_code) }

before do
allow_any_instance_of(Io::Flow::V0::Clients::Hs10)
.to(receive(:get).with('mejuridevs', limit: 100, offset: 0).and_return([hs_code_data]))
allow_any_instance_of(Io::Flow::V0::Clients::Hs10)
.to(receive(:get).with('mejuridevs', limit: 100, offset: 100).and_return([]))
end

it 'Update variant HS code' do
FlowcommerceSpree::ImportItemsHsCodes.run
variant = Spree::Variant.find_by(sku: hs_code_data.item.number)
expect(variant.flow_data['hs_code']).to(eq(hs_code_data.code[0..5]))
end

it 'Calls VariantService#update_flow_classification method' do
expect_any_instance_of(VariantService).to(receive(:update_flow_classification)
.with([hs_code_data.item.number]))
FlowcommerceSpree::ImportItemsHsCodes.run
end
end
end
35 changes: 35 additions & 0 deletions spec/support/tasks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

require 'rake'

# Task names should be used in the top-level describe, with an optional
# "rake "-prefix for better documentation. Both of these will work:
#
# 1) describe "foo:bar" do ... end
#
# 2) describe "rake foo:bar" do ... end
#
# Favor including "rake "-prefix as in the 2nd example above as it produces
# doc output that makes it clear a rake task is under test and how it is
# invoked.
module TaskFormat
extend ActiveSupport::Concern

included do
let(:task_name) { self.class.top_level_description.sub(/\Arake /, '') }
let(:tasks) { Rake::Task } # Make the Rake task available as `task` in your examples:
subject(:task) { tasks[task_name] }
end
end

RSpec.configure do |config| # Tag Rake specs with `:task` metadata or put them in the spec/tasks dir
config.define_derived_metadata(file_path: %r{/spec/tasks/}) do |metadata|
metadata[:type] = :task
end

config.include TaskFormat, type: :task

config.before(:suite) do
Rails.application.load_tasks
end
end
45 changes: 45 additions & 0 deletions spec/tasks/spree_variant_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

require 'rails_helper'
require 'csv'

describe 'rake spree_variants', type: :task do
let(:variant) { create(:base_variant, :with_flow_data) }

describe 'import_flow_hs_code_from_csv' do
let(:hs_code) { '711319' }
let(:stubed_csv_content) { [variant.sku, hs_code, variant.product.id, variant.product.name] }
let(:stubed_csv) { CSV.new("item_number,hs6,product_id,item_name\n#{stubed_csv_content.join(',')}", headers: true) }
let(:run_codes_rake_task) do
Rake::Task['spree_variant:import_flow_hs_code_from_csv'].reenable
Rake.application.invoke_task('spree_variant:import_flow_hs_code_from_csv')
end

before(:each) do
allow(CSVUploader).to(receive(:download_url).and_return('https://s3.amazonaws.com/test/script/flow_hs_codes.csv'))
allow_any_instance_of(URI::HTTPS).to(receive(:open))

allow(CSV).to(receive(:new).and_return(stubed_csv))
end

it 'updates variant`s flow data with the hs_code' do
expect(variant.flow_data['hs_code']).to(be_blank)
run_codes_rake_task
expect(variant.reload.flow_data['hs_code']).to(eq(hs_code))
end

it 'Calls VariantService#update_flow_classification method' do
expect_any_instance_of(VariantService).to(receive(:update_flow_classification).with([variant.sku]))
run_codes_rake_task
end

context 'when no hs_code is present' do
let(:stubed_csv_content) { [variant.sku, '', variant.product.id, variant.product.name] }

it 'does not update variant' do
run_codes_rake_task
expect(variant.reload.flow_data['hs_code']).to(be_blank)
end
end
end
end