Class: Edi::Amazon::ConfirmMessageProcessor
- Inherits:
-
BaseEdiService
- Object
- BaseService
- BaseEdiService
- Edi::Amazon::ConfirmMessageProcessor
- Defined in:
- app/services/edi/amazon/confirm_message_processor.rb
Constant Summary collapse
- CARRIER_TO_CARRIER_CODE_MAP_HASH =
{ 'Canadapost': 'Canada Post', 'Canpar': 'Canpar', 'FedEx': 'FedEx', 'Purolator': 'PUROLATOR', 'Ups': 'UPS', 'UPS': 'UPS', 'Usps': 'USPS', 'USPS': 'USPS', 'RlCarriers': 'R+L', 'AmazonSeller': 'Amazon' }
Constants included from Edi::AddressAbbreviator
Edi::AddressAbbreviator::MAX_LENGTH
Instance Attribute Summary
Attributes inherited from BaseEdiService
Instance Method Summary collapse
- #acknowledge_order(order) ⇒ Object
- #back_order(order) ⇒ Object
- #build_acknowledge_message(shipment, order_items) ⇒ Object
- #cancel_order(order) ⇒ Object
- #confirm_invoice(invoice) ⇒ Object
- #map_carrier_code_from_hw_carrier(carrier) ⇒ Object
-
#map_order_items_to_shipments(delivery, order_hash) ⇒ Object
Maps each Amazon order item to exactly one shipment/tracking number, then groups by shipment.
- #process(confirm_message, category, order = nil) ⇒ Object
- #resolve_amazon_order_item_id(parent_li, order_hash) ⇒ Object
Methods inherited from BaseEdiService
#duplicate_po_already_notified?, #initialize, #mark_duplicate_po_as_notified, #report_order_creation_issues, #safe_process_edi_communication_log
Methods included from Edi::AddressAbbreviator
#abbreviate_street, #collect_street_originals, #record_address_abbreviation_notes
Methods inherited from BaseService
#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #options, #tagged_logger
Constructor Details
This class inherits a constructor from Edi::BaseEdiService
Instance Method Details
#acknowledge_order(order) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 21 def acknowledge_order(order) delivery = order.deliveries.first # Ship confirm when we label via Heatwave (including Amazon Buy Shipping labels purchased through our system). # When using Veeqo or Buy Amazon Shipping directly through Seller Central, it already handles the ship confirmation. return unless delivery.ship_labeled_via_heatwave? # Buy Shipping V2 purchaseShipment already notifies Amazon the order is shipped. # A separate confirmShipment is redundant and fails with "PackageToUpdateNotFound". if delivery.carrier == 'AmazonSeller' Rails.logger.info("[Amazon ConfirmMessageProcessor] Skipping confirmShipment for AMZBS order #{order.id} — purchaseShipment already confirmed") return end order_hash = JSON.parse(order.).with_indifferent_access shipment_item_groups = map_order_items_to_shipments(delivery, order_hash) return if shipment_item_groups.empty? shipment_item_groups.map do |shipment, order_items| m = (shipment, order_items) process(m, :order_acknowledge, order) end end |
#back_order(order) ⇒ Object
133 134 135 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 133 def back_order(order) # do nothing for Amazon SC. end |
#build_acknowledge_message(shipment, order_items) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 106 def (shipment, order_items) # For Amazon Buy Shipping labels, use the actual carrier from metadata effective_carrier = if shipment.carrier == 'AmazonSeller' && shipment.respond_to?(:amz_carrier) && shipment.amz_carrier.present? shipment.amz_carrier else shipment.carrier end { packageDetail: { packageReferenceId: shipment.id.to_s, carrierCode: map_carrier_code_from_hw_carrier(effective_carrier), carrierName: effective_carrier, shippingMethod: shipment.delivery.shipping_method_friendly, trackingNumber: shipment.tracking_number, shipDate: (shipment.delivery.shipped_date || shipment.delivery.ship_labeled_at || Time.now)&.iso8601, orderItems: order_items }, codCollectionMethod: "DirectPayment", marketplaceId: orchestrator.marketplace } end |
#cancel_order(order) ⇒ Object
137 138 139 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 137 def cancel_order(order) # do nothing for Amazon SC. end |
#confirm_invoice(invoice) ⇒ Object
141 142 143 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 141 def confirm_invoice(invoice) # do nothing for Amazon SC. end |
#map_carrier_code_from_hw_carrier(carrier) ⇒ Object
129 130 131 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 129 def map_carrier_code_from_hw_carrier(carrier) CARRIER_TO_CARRIER_CODE_MAP_HASH[carrier.to_sym] || 'Other' end |
#map_order_items_to_shipments(delivery, order_hash) ⇒ Object
Maps each Amazon order item to exactly one shipment/tracking number, then groups
by shipment. This is item-centric rather than shipment-centric, which correctly
handles multibox kits (single item shipped across multiple packages).
The SP-API confirmShipment endpoint requires each orderItemId to appear in exactly
one package confirmation. Sending the same item in multiple calls causes 400 errors
("order already fulfilled"). This method ensures every item gets confirmed exactly once.
Algorithm:
- For each completed shipment, count how many components of each parent line item it contains
- Assign each parent to the shipment with the most components (its "primary" package)
- Group parents by their primary shipment
Returns: { Shipment => [{ orderItemId:, quantity: }, ...] }
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 58 def map_order_items_to_shipments(delivery, order_hash) all_shipments = delivery.shipments.completed.top_level .includes(shipment_contents: { line_item: :parent }).to_a return {} if all_shipments.empty? shipment_by_id = all_shipments.index_by(&:id) # Count components of each parent line item in each shipment. # For non-kit items (no parent), the line item itself acts as the parent. parent_shipment_component_counts = Hash.new { |h, k| h[k] = Hash.new(0) } all_shipments.each do |shipment| shipment.shipment_contents.each do |sc| parent_id = (sc.line_item.parent || sc.line_item).id parent_shipment_component_counts[parent_id][shipment.id] += sc.quantity end end parent_line_items = delivery.line_items.goods.parents_only.to_a groups = Hash.new { |h, k| h[k] = [] } parent_line_items.each do |parent_li| counts = parent_shipment_component_counts[parent_li.id] next if counts.empty? best_shipment_id = counts.max_by { |_sid, count| count }.first shipment = shipment_by_id[best_shipment_id] next unless shipment order_item_id = resolve_amazon_order_item_id(parent_li, order_hash) next unless order_item_id groups[shipment] << { orderItemId: order_item_id, quantity: parent_li.quantity } end groups end |
#process(confirm_message, category, order = nil) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 145 def process(, category, order = nil) edi_log = nil EdiCommunicationLog.transaction do edi_log = EdiCommunicationLog.create! partner: orchestrator.partner, category: category, data: .to_json, data_type: 'json', file_info: { lines_confirmed: [:packageDetail][:orderItems].size }, transmit_datetime: Time.current end edi_log.edi_documents.create!(order: order) if order.present? edi_log end |
#resolve_amazon_order_item_id(parent_li, order_hash) ⇒ Object
98 99 100 101 102 103 104 |
# File 'app/services/edi/amazon/confirm_message_processor.rb', line 98 def resolve_amazon_order_item_id(parent_li, order_hash) order_item_id = parent_li.edi_reference if order_item_id&.include?(order_hash[:AmazonOrderId]) order_item_id = order_hash[:OrderItems][(parent_li.edi_line_number || 1) - 1]&.dig(:OrderItemId) end order_item_id end |