Last active
June 10, 2020 10:21
-
-
Save iarie/770efe6cfdecdeca2dc5d4cca27bdfbf to your computer and use it in GitHub Desktop.
all in one gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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