Class: Order::CreateVcProcurementOrdersFromCsv

Inherits:
BaseService
  • Object
show all
Defined in:
app/services/order/create_vc_procurement_orders_from_csv.rb

Defined Under Namespace

Classes: Result

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseService

#log_debug, #log_error, #log_info, #log_warning, #options, #tagged_logger

Constructor Details

#initialize(options = {}) ⇒ CreateVcProcurementOrdersFromCsv

Returns a new instance of CreateVcProcurementOrdersFromCsv.



8
9
10
11
12
13
14
# File 'app/services/order/create_vc_procurement_orders_from_csv.rb', line 8

def initialize(options = {})
  @options = options
  @logger = options[:logger] || Rails.logger
  @csv_attachment = options[:csv_attachment] # this comes in as an http form attachment
  @csv_file_path = @csv_attachment.tempfile.path.to_s # this is the temp file path
  @user = options[:user] # this is the user that is doing the import
end

Instance Attribute Details

#csv_attachmentObject

Returns the value of attribute csv_attachment.



2
3
4
# File 'app/services/order/create_vc_procurement_orders_from_csv.rb', line 2

def csv_attachment
  @csv_attachment
end

#csv_file_pathObject

Returns the value of attribute csv_file_path.



2
3
4
# File 'app/services/order/create_vc_procurement_orders_from_csv.rb', line 2

def csv_file_path
  @csv_file_path
end

#loggerObject

Returns the value of attribute logger.



2
3
4
# File 'app/services/order/create_vc_procurement_orders_from_csv.rb', line 2

def logger
  @logger
end

#userObject

Returns the value of attribute user.



2
3
4
# File 'app/services/order/create_vc_procurement_orders_from_csv.rb', line 2

def user
  @user
end

Instance Method Details

#processObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/services/order/create_vc_procurement_orders_from_csv.rb', line 16

def process
  errors = []
  orders = []
  info_msgs = []
  CSV.foreach(csv_file_path, liberal_parsing: true, headers: true, encoding: 'iso-8859-1:utf-8') do |row|
    Order.transaction do
      if row.first[1].present? # skip blank rows
        po_number = row['PO']
        vendor_code = row['Vendor']
        ship_to_location = row['Ship to location'] # 'MSP6 - LAKEVILLE, MN'
        ship_to_code = ship_to_location.split(' - ').first # MSP6
        asin = row['ASIN']
        sku = row['Model Number']
        window_start = begin
          Date.strptime(row['Window Start'] || row['Window start'], '%m/%d/%Y')
        rescue StandardError
          errors << 'Cannot parse date from Window Start column'
        end
        window_end = begin
          Date.strptime(row['Window End'] || row['Window end'], '%m/%d/%Y')
        rescue StandardError
          errors << 'Cannot parse date from Window End column'
        end
        qty_requested = row['Quantity Requested'].to_i
        unit_cost = row['Unit Cost'].to_f
        is_bulk_order = (window_start > 1.week.from_now) # determine Amazon VC customer record for long-term bulk order vs short-term replenishment order based on 1 week from now shipping window
        vendor_code_to_customer_id_map = is_bulk_order ? CustomerConstants::AMAZON_VENDOR_CENTRAL_BULK_ORDER_CUSTOMER_ID_BY_VENDOR_CODE : CustomerConstants::AMAZON_VENDOR_CENTRAL_CUSTOMER_ID_BY_VENDOR_CODE # get appropruate type of Amazon VC customer
        customer = Customer.find(vendor_code_to_customer_id_map[vendor_code.to_sym]) # get customer from vendor code
        errors << "Cannot find a corresponding Amazon VC procurement customer for vendor code #{vendor_code}!" unless customer.present?
        # here we want to only process non-cancelled pending orders, because multiple lines can be added to the same order within the CSV, but we don't want to touch an order that is already processing, i.e. beyond pending, so find or create the order in pending state
        order = customer&.orders&.not_cancelled&.where(edi_po_number: po_number)&.first || customer&.orders&.new(edi_po_number: po_number, state: 'pending') if customer
        unless customer.present? && order.pending?
          errors << "Order  #{order.reference_number} with PO #{po_number} in state: #{order.state} already exists! Skipping import of ASIN: #{asin} (qty #{qty_requested}) for PO #{po_number}. If you are reimporting, please cancel this order and try importing again."
        end
        ci = customer&.catalog&.catalog_items&.by_asins([asin])&.first # find corresponding Amazon VC customer catalog item with the ASIN
        errors << ("Item with ASIN: #{asin} (SKU: #{sku}) not found in customer catalog #{customer&.catalog&.name}.") if ci&.id.nil?
        # here we don't want to keep on adding more quantity if re-importing the same CSV, so just find or create the line item and set its quantity and price
        unless ci&.id.nil?
          line_updated = false
          li = order&.line_items&.detect do |li|
            li.item&.amazon_asin == asin
          end || order.add_line_item(catalog_item_id: ci.id, quantity: qty_requested, price: unit_cost, discounted_price: unit_cost, edi_unit_cost: unit_cost)
        end
        line_updated = li.update(quantity: qty_requested, price: unit_cost, discounted_price: unit_cost, edi_unit_cost: unit_cost) unless li&.id.nil? # we want everything to work
        # report errors on line item update if line item is present but not updated
        errors << ("Line item with ASIN: #{asin} (SKU: #{sku}) in order #{order.reference_number} with PO #{po_number} did not update correctly: #{li.errors_to_s}.") if !line_updated && li&.id.present?
        if customer.present? && order&.pending? && ci&.id.present? && li&.id.present? && line_updated # finally, assuming everything went well, set the order address if possible and flag if address was not found by location ship_to_code, add notes etc.
          order.shipping_address_id = customer.addresses.where(person_name_override: ship_to_code)&.first&.id
          order.requested_ship_on_or_after = window_start
          order.requested_ship_before = window_end
          order.save
          order.reload
          order.activities.create(new_note: "Created or updated by import of #{csv_attachment&.original_filename} on #{DateTime.now} by #{user.name}.")
          order.activities.create(new_note: "Ship Window: #{window_start} - #{window_end}")
          errors += order.errors.full_messages unless order.valid?
          orders << order
          order_msg = "Order #{order.reference_number} with PO <strong>#{po_number}</strong>, created or updated in customer #{customer.name}, with line items: #{order.line_items.parents_only.to_a.map do |l|
            "#{l.quantity.abs} x #{l.sku}/#{l&.item&.amazon_asin} at unit cost: $#{li.discounted_price}"
          end.join(', ')}."
          info_msgs << order_msg
          order.activities.create(new_note: order_msg)
          if order&.shipping_address.nil? # report if address was not found by location ship_to_code, we can't create the address because we only get a basic location snippet in the CSV
            ship_to_msg = "<strong>Ship to location #{ship_to_location} not found in customer addresses</strong>, please create it and set as the shipping address for this order."
            order.activities.create(new_note: ship_to_msg)
            info_msgs << ship_to_msg
          end
        end
      end
    rescue StandardError => e
      offending_line = e.backtrace.first # Gets the first line of the backtrace (most likely the line that caused the error)
      errors << "Exception: #{e} at #{offending_line}, offending row: #{row}"
      ErrorReporting.error(e, line: offending_line)
    end
  end
  orders.uniq!
  orders.each do |order|
    if order.valid?
      order.retrieve_shipping_costs
      order.reload
      order.payments.build.tap do |payment|
        payment.category = Payment::PO
        payment.currency = order.currency
        payment.amount = order.total
        payment.state = 'authorized'
        payment.po_number = order.edi_po_number
        payment.delivery = order.deliveries.quoting.first
        payment.save!
      end
      order.request_estimated_packaging!
    end
  rescue StandardError => e # report any errors
    errors << "Exception in order #{order.reference_number} with PO #{order.edi_po_number}: #{e}"
    ErrorReporting.error(e)
  end # End begin rescue block
  Result.new(orders: orders, info_msgs: info_msgs, errors: errors)
end