Skip to content

Instantly share code, notes, and snippets.

@iarie
Last active June 10, 2020 10:21
Show Gist options
  • Save iarie/770efe6cfdecdeca2dc5d4cca27bdfbf to your computer and use it in GitHub Desktop.
Save iarie/770efe6cfdecdeca2dc5d4cca27bdfbf to your computer and use it in GitHub Desktop.
all in one gist
interval = Date.new(2020, 5, 1)..Date.new(2020, 6, 7)
module FixStuckShipments
FINAL_STATES = %w[
shipped
canceled
].freeze
SHIPMENT_STATUS_MAP = {
'Complete' => 'shipped',
'Awaiting Return' => 'shipped',
'Credited' => 'shipped',
'In Picking' => 'picking',
'In Process' => 'processing',
'Back Ordered' => 'manual',
'Shipped' => 'shipped',
'Cancelled' => 'canceled'
}.freeze
# Search for a correct state using multiple approaches
def self.full_search(status, interval = Date.new(2020, 5, 1).all_month, update: false)
puts "Starting Check for #{status}"
stock_location_ids = Spree::Supplier.alphabroder.stock_location_ids
shipments = Spree::Shipment.joins(:order).where(
spree_orders: { completed_at: interval },
spree_shipments: {
state: status,
stock_location_id: stock_location_ids
}
).where(
'spree_shipments.deleted_at IS NULL'
)
puts 'Searching'
# collecting problem shipments
shipments_and_states = shipments.map do |shipment|
clues = []
print('.')
wh = shipment.stock_location.admin_name
# 1st search: by shipment's number
# shipment have po number but it's not guaranteed to be correct
api_shipment = get_status(shipment.number)
if api_shipment.present?
found = api_shipment.find { |x| x[:wh] == wh }
if found
status_from_api = found[:status]
else
api_whs = api_shipment.map { |x| x[:wh] }
clues << "Warehouse From API mismatch. #{wh} <=> #{api_whs}"
end
else
clues << 'Bad Shipment Number'
end
# 2nd search: by siblings's number
# sibling shipments can have correct po number by which we can fetch correct state from api
siblings = shipment.order.shipments.where(
stock_location_id: stock_location_ids
).where.not(id: shipment.id)
if siblings.any?
clues << 'Multiple AB Shipments'
else
clues << 'Single AB Shipment'
end
sibling_states = siblings.map do |sibling|
api_states = get_status(sibling.number)
{
id: sibling.id,
number: sibling.number,
wh: sibling.stock_location.admin_name,
state: sibling.state,
api_states: api_states
}
end
states = sibling_states.flat_map {|x| x[:api_states] }
if states.present?
found = states.find do |x|
x[:wh] == wh
end
if found
status_from_siblings = found[:status]
else
api_whs = states.map { |x| x[:wh] }
clues << "Possible WH Mismatch. #{wh} <=> #{api_whs}"
end
else
clues << "No data from siblings"
end
# 3rd search: by shipment's order supplier orders numbers
# shipment can have correct po number within related supplier_orders
# altough there is no guarateed direct relation so we check all ab supplier orders
so_shipments = shipment.order.supplier_orders.where(supplier_name: 'alphabroder').pluck(:order_number).flat_map do |pon|
get_status(pon)
end
if so_shipments.present?
found = so_shipments.find do |x|
x[:wh] == wh
end
if found
status_from_so = found[:status]
else
api_whs = so_shipments.map { |x| x[:wh] }
clues << "Possible WH Mismatch. #{wh} <=> #{api_whs}"
end
else
clues << 'No SO'
end
change_status_to = status_from_api || status_from_siblings || status_from_so
{
id: shipment.id,
order_number: shipment.order.number,
number: shipment.number,
current_state: shipment.state,
wh: wh,
api_shipment: api_shipment,
siblings: sibling_states,
so_shipments: so_shipments,
status_from_api: status_from_api,
status_from_siblings: status_from_siblings,
status_from_so: status_from_so,
change_status_to: change_status_to,
clues: clues
}
end
print("\n")
plan = shipments_and_states.group_by { |x| x[:change_status_to] }.transform_values(&:size)
puts "Result: #{plan}"
return shipments_and_states unless update
update!(shipments_and_states, state_was: status, problem: 'out_of_sync')
end
private
def self.query(status, interval)
puts 'Fetching data'
stock_location_ids = Spree::Supplier.alphabroder.stock_location_ids
order_ids = Spree::Order.joins(:shipments).where(
completed_at: interval,
spree_shipments: {
state: status,
stock_location_id: stock_location_ids
}
).where(
'spree_shipments.deleted_at IS NULL'
).ids.uniq
Spree::Order.includes(:supplier_orders, shipments: [:supplier_shipments, :stock_location]).find(order_ids)
end
def self.get_status(po_number)
r = Supplier::Alphabroder::OrderStatusV2::GetOrderStatus.new.call(po_number)
doc = Nokogiri::XML(r.body)
nodes = doc.xpath("//order")
pnode = Supplier::Alphabroder::OrderStatusV2::OrderXmlNode.new(nodes)
pnode.ship_method_nodes.map do |n|
{
po_number: po_number,
wh: n.warehouse,
status: SHIPMENT_STATUS_MAP[n.ship_status]
}
end
end
def self.update!(shipments_and_states, state_was:, problem:)
puts 'Updating'
shipments_to_update = shipments_and_states.select { |x| x[:change_status_to].in?(FINAL_STATES) }
shipments_to_update.map do |x|
ship = Spree::Shipment.find(x[:id])
order_number = ship.order.number
ship.transaction do
ship.update_columns(state: x[:change_status_to], updated_at: Time.current)
ship.supplier_shipments.update_all(status: x[:change_status_to], updated_at: Time.current)
end
print('.')
[order_number, ship.id, state_was, x[:change_status_to], problem]
end.sort_by {|i| [i[0], i[1]]}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment