Class: Edi::Walmart::InventoryMessageProcessor

Inherits:
BaseEdiService show all
Defined in:
app/services/edi/walmart/inventory_message_processor.rb

Constant Summary

Constants included from AddressAbbreviator

AddressAbbreviator::MAX_LENGTH

Instance Attribute Summary

Attributes inherited from BaseEdiService

#orchestrator

Instance Method Summary collapse

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 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

#append_catalog_item(messages, ci) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'app/services/edi/walmart/inventory_message_processor.rb', line 89

def append_catalog_item(messages, ci)
  begin
    return unless ci.reported_vendor_sku.present?

    discontinued = ci.discontinued? || ci.pending_discontinue? || ci.in_hide_from_feed_state?
    available_date = Date.today
    stock = (discontinued ? 0 : ci.reported_stock(use_alternate_warehouse: false, safety_stock: 0))
    available_date += 100.years if discontinued
    if stock.zero? && !discontinued
      # See when we would get stock
      available_dates = []
      begin
        # Use depth-limited version to prevent infinite recursion
        ci.next_available_by_warehouse_with_depth_limit(use_alternate_warehouse: false, max_depth: 10).each do |warehouse_name, on_order_data|
          next unless on_order_data && on_order_data.next_available_date

          available_dates << on_order_data.next_available_date
        end
      rescue SystemStackError => e
        # Log the recursion error with detailed context
        ErrorReporting.error(e,
          catalog_item_id: ci.id,
          partner: orchestrator.partner,
          catalog_item_sku: ci.item&.sku,
          error_type: 'stack_level_too_deep',
          message: 'Infinite recursion detected in next_available_by_warehouse method')
        # NOTE: available_date will be set to fallback value in the next line
      end
      available_date = (available_dates.any? ? available_dates.min : Date.today + 3.months) # set a reasonable default for an out of stock item when no next available
    end
    message = {
        sku: ci.reported_vendor_sku,
        quantity: {
          unit: 'EACH',
          amount: stock
        },
        inventoryAvailableDate: available_date.to_s
    }
    messages << message
  rescue StandardError => e
    logger.error "Error processing catalog item #{ci.id} for partner #{orchestrator.partner}: #{e.message}"
    ErrorReporting.error(e, catalog_item_id: ci.id, partner_id: orchestrator.partner.id)
  end
  messages
end

#append_catalog_items(catalog_items) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'app/services/edi/walmart/inventory_message_processor.rb', line 78

def append_catalog_items(catalog_items)
  messages = []
  catalog_items.find_each do |ci|
    ErrorReporting.scoped(catalog_item_id: ci.id) do
      logger.info "Processing catalog item #{ci.id} for partner #{orchestrator.partner}"
      append_catalog_item(messages, ci)
    end
  end
  messages
end

#build_json(catalog_items: nil, states: nil) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'app/services/edi/walmart/inventory_message_processor.rb', line 39

def build_json(catalog_items: nil, states: nil)
  # see: https://github.com/amzn/selling-partner-api-models/blob/main/schemas/feeds/listings-feed-schema-v2.json
  catalog_items ||= load_catalog_items(states:)
  logger.info "#{catalog_items.size} items in inventory feed payload"

  # https://developer.walmart.com/us-marketplace/docs/bulk-inventory#sample-request
  # {
  #   "InventoryHeader": {
  #     "version": "1.4"
  #   },
  #   "Inventory": [
  #     {
  #       "sku": "SKU_012345",
  #       "quantity": {
  #         "unit": "EACH",
  #         "amount": 10
  #       },
  #       "inventoryAvailableDate": "YYYY-MM-DD"
  #     },
  #     {
  #       "sku": "SKU_678901",
  #       "quantity": {
  #         "unit": "EACH",
  #         "amount": 20
  #       },
  #       "inventoryAvailableDate": "YYYY-MM-DD"
  #     }
  #   ]
  # }

  feed_hash = {
    InventoryHeader: {
      version: '1.4'
    },
    Inventory: append_catalog_items(catalog_items)
  }
  feed_hash.to_json
end

#load_catalog_items(states: nil, use_delta_since_last_message: false) ⇒ Object



29
30
31
32
33
34
35
36
37
# File 'app/services/edi/walmart/inventory_message_processor.rb', line 29

def load_catalog_items(states: nil, use_delta_since_last_message: false)
  states ||= %w[active require_vendor_update pending_vendor_update pending_discontinue]
  catalog_items = orchestrator.customer.catalog
                              .catalog_items
                              .for_edi_feeds_by_states(states)
                              .with_item.eager_load(:store_item, :item)
  catalog_items = catalog_items.edi_delta_feeds_for_partner_inventory_advice(orchestrator.partner) if use_delta_since_last_message
  catalog_items.order(StoreItem[:qty_available])
end

#process(catalog_items: nil, states: nil, use_delta_since_last_message: false) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'app/services/edi/walmart/inventory_message_processor.rb', line 5

def process(catalog_items: nil, states: nil, use_delta_since_last_message: false)
  # see: https://developer.walmart.com/us-marketplace/docs/inventory-api-overview
  # see: https://developer.walmart.com/us-marketplace/reference/
  # see: https://www.postman.com/walmart-developer-portal/us-collection/request/j6u6dtg/bulk-item-inventory-update?tab=configurations
  logger.info "Creating inventory feed for partner #{orchestrator.partner}"
  catalog_items ||= load_catalog_items(states:, use_delta_since_last_message:)
  return :empty_set unless catalog_items.present?

  ecl = nil
  EdiCommunicationLog.transaction do
    data_json = build_json(catalog_items:, states:)
    ecl = EdiCommunicationLog.create_outbound_file_from_data(
      data: data_json,
      data_type: 'json',
      file_extension: 'json',
      partner: orchestrator.partner,
      category: 'inventory_advice',
      resources: catalog_items,
      file_info: {}
    )
  end
  ecl
end