Skip to content

Instantly share code, notes, and snippets.

@raeno
Created November 12, 2018 15:52
Show Gist options
  • Save raeno/c0da28cdb0779ac18d38ed63871cbfab to your computer and use it in GitHub Desktop.
Save raeno/c0da28cdb0779ac18d38ed63871cbfab to your computer and use it in GitHub Desktop.
Amazon MWS examples
# frozen_string_literal: true
module Amazon
class InboundShipmentApi
include Logging
include WithThrottling
ACTIVE_SHIPMENT_STATUSES = %w( WORKING SHIPPED IN_TRANSIT DELIVERED CHECKED_IN RECEIVING CLOSED CANCELLED DELETED ERROR).freeze
DEFAULT_SHIPMENTS_RANGE = 24.months.ago
def self.build(amazon_config, options = {})
inbound_shipment_client = MWS.fulfillment_inbound_shipment(amazon_config)
new(inbound_shipment_client, options)
end
def initialize(inbound_shipment_client, options = {})
@inbound_shipment_client = inbound_shipment_client
@ship_from_address = options.fetch(:ship_from_address, {})
end
def get_prep_instructions_for_sku(*skus)
response = inbound_shipment_client.get_prep_instructions_for_sku('US', skus)
response.parse
rescue => e
log(e.message, :error)
nil
end
def create_shipment_plan(items)
shipment_plans = inbound_shipment_client.create_inbound_shipment_plan(ship_from_address, items)
shipment_plans.parse
rescue => e
log(e.message, :error)
nil
end
def create_shipment(shipment_id, header, items_to_ship)
response = inbound_shipment_client.create_inbound_shipment(shipment_id, header, inbound_shipment_items: items_to_ship)
ShipmentApiResult.new response: response, success: true
rescue => e
ShipmentApiResult.new response: e.response, success: false, errors: [e.message, e.response.body]
end
def update_shipment(shipment_id, header, items_to_ship)
response = inbound_shipment_client.update_inbound_shipment(shipment_id, header, inbound_shipment_items: items_to_ship)
ShipmentApiResult.new(response: response, success: true)
rescue => e
ShipmentApiResult.new(response: e.response, success: false, errors: [e.message, e.response.body])
end
def fetch_shipments(statuses: ACTIVE_SHIPMENT_STATUSES,
date_from: DEFAULT_SHIPMENTS_RANGE,
date_to: Time.zone.now)
params = {
shipment_status_list: statuses,
last_updated_before: date_to.to_s(:iso8601),
last_updated_after: date_from.to_s(:iso8601)
}
shipments_response = inbound_shipment_client.list_inbound_shipments(params).parse
shipments = [shipments_response]
while shipments_response['NextToken']
throttle 30, per: :minute do
token = shipments_response['NextToken']
shipments_response = inbound_shipment_client.list_inbound_shipments_by_next_token(token).parse
shipments << shipments_response
end
end
shipments
end
private
attr_reader :inbound_shipment_client, :ship_from_address
end
end
# frozen_string_literal: true
module Amazon
class InventoryApi
include Logging
include WithThrottling
AMAZON_REPORT_NAME = '_GET_FBA_MYI_UNSUPPRESSED_INVENTORY_DATA_'
INVENTORY_DATA_KEY = '_GET_FBA_MYI_UNSUPPRESSED_INVENTORY_DATA_'
def self.build(config = {})
reports_client = MWS.reports(config)
new(reports_client)
end
def initialize(reports_client)
@reports_client = reports_client
end
# only asks Amazon to prepare report, it can take up to few hours
# to make the report on Amazon side. Fetch reports with #fetch_reports
def request_report
reports_client.request_report(AMAZON_REPORT_NAME)
end
def fetch_reports(reports_range)
start_date = reports_range.ago.to_date.to_s(:iso8601)
reports_lists = fetch_reports_lists(start_date)
reports_lists.compact.flat_map { |list| extract_reports(list) }
end
def fetch_report(report_id)
log "Fetching report data for #{report_id}"
report = reports_client.get_report(report_id)
return nil unless report
report.body
end
private
attr_reader :reports_client
def fetch_reports_lists(start_date)
log "Getting initial reports list from Amazon"
reports_list = fetch_initial_reports_list(start_date)
return [] unless reports_list
all_lists = [reports_list]
while reports_list['NextToken']
log "Getting following reports list from Amazon"
throttle 30, per: :minute do
reports_list = fetch_next_reports_list reports_list['NextToken']
end
all_lists << reports_list
end
all_lists
end
def extract_reports(list)
report_info = list['ReportInfo']
return [] unless report_info
report_info.select { |r| r['ReportType'] == INVENTORY_DATA_KEY }
end
def fetch_initial_reports_list(start_date)
reports_list = reports_client.get_report_list(available_from_date: start_date)
reports_list.parse
rescue => e
log "Error while fetching initial reports list: #{e.message}", :error
nil
end
def fetch_next_reports_list(token)
reports_list = reports_client.get_report_list_by_next_token(token)
reports_list.parse
rescue => e
log "Error while fetching following reports list:: #{e.message}", :error
nil
end
end
end
# frozen_string_literal: true
module Amazon
module Parsers
class ShipmentPlansParser
attr_reader :address_parser, :prep_instructions_parser
def self.build
address_parser = Amazon::Parsers::AddressParser.new
new(address_parser)
end
def initialize(address_parser)
@address_parser = address_parser
end
def parse(response)
return ShipmentPlansResult.new([], false, ['Invalid response in creating shipment plan']) unless response
plans = response.dig 'InboundShipmentPlans', 'member'
result = Array.wrap(plans).map do |plan_body|
build_shipment_plan(plan_body)
end
ShipmentPlansResult.new(result, true, [])
rescue => e
ShipmentPlansResult.new(result, false, [e.message])
end
private
def build_shipment_plan(plan)
shipment_plan = OpenStruct.new
shipment_plan.destination_fulfillment_center_id = plan.dig 'DestinationFulfillmentCenterId'
shipment_plan.label_prep_type = plan.dig 'LabelPrepType'
shipment_plan.shipment_id = plan.dig 'ShipmentId'
shipment_plan.ship_to_address = parse_address plan
items = plan.dig 'Items', 'member'
shipment_plan.items = parse_items_on_shipment_plan items
shipment_plan
end
def parse_items_on_shipment_plan(items)
return [] unless items
Array.wrap(items).map do |item|
prep_details = item.dig 'PrepDetailsList', 'PrepDetails'
OpenStruct.new(
fulfillment_network_sku: item.dig('FulfillmentNetworkSKU'),
quantity: item.dig('Quantity'),
seller_sku: item.dig('SellerSKU'),
prep_details_list: parse_preparation_details(prep_details)
)
end
end
def parse_preparation_details(preparation_details)
return [] unless preparation_details
Array.wrap(preparation_details).map do |details|
owner = details.dig 'PrepOwner'
instruction = details.dig 'PrepInstruction'
OpenStruct.new prep_owner: owner, prep_instruction: instruction
end
end
def parse_address(response)
address_response = response.dig 'ShipToAddress'
raise ArgumentError, 'Invalid shipping address' unless address_response
address_parser.parse(address_response)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment