Class: RmaItem

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable, Models::LiquidMethods
Defined in:
app/models/rma_item.rb

Overview

== Schema Information

Table name: rma_items
Database name: primary

id :integer not null, primary key
arrival_datetime :datetime
inspection_notes :text
liable :string
received_date :date
replacement_required :boolean default(FALSE), not null
restocking_reason :string(255)
returned_item_location :string(255)
returned_item_quantity :integer
returned_reason :string(255)
state :string(255)
tech_examination_needed :boolean default(FALSE), not null
under_warranty :boolean
will_not_be_returned :boolean default(FALSE), not null
created_at :datetime
updated_at :datetime
creator_id :integer
credit_order_id :integer
replacement_order_id :integer
returned_item_id :integer
returned_line_item_id :integer
rma_id :integer
updater_id :integer

Indexes

idx_on_rma_id_returned_reason (rma_id,returned_reason)
idx_rma_id_tech_examination_needed (rma_id,tech_examination_needed)
idx_rma_id_wnb_returned (rma_id,will_not_be_returned)
index_rma_items_on_restocking_reason (restocking_reason)
index_rma_items_on_returned_line_item_id (returned_line_item_id)
index_rma_items_on_returned_reason (returned_reason)
ret_item_id_ret_line_item_id (returned_item_id,returned_line_item_id)
rma_id_replacement_required (rma_id,replacement_required)
rma_id_state (rma_id,state)

Foreign Keys

fk_rails_... (restocking_reason => rma_reason_codes.code) ON UPDATE => cascade
fk_rails_... (returned_reason => rma_reason_codes.code) ON UPDATE => cascade

Constant Summary collapse

LIABLE_OPTIONS =
%w[WARMLYYOURS CUSTOMER].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has one collapse

Has many collapse

Has and belongs to many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#new_recordObject

Returns the value of attribute new_record.



113
114
115
# File 'app/models/rma_item.rb', line 113

def new_record
  @new_record
end

#original_qtyObject

Returns the value of attribute original_qty.



113
114
115
# File 'app/models/rma_item.rb', line 113

def original_qty
  @original_qty
end

Returns the value of attribute print_label.



113
114
115
# File 'app/models/rma_item.rb', line 113

def print_label
  @print_label
end

#qty_to_receiveObject

Returns the value of attribute qty_to_receive.



113
114
115
# File 'app/models/rma_item.rb', line 113

def qty_to_receive
  @qty_to_receive
end

#replacement_item_skuObject

Returns the value of attribute replacement_item_sku.



113
114
115
# File 'app/models/rma_item.rb', line 113

def replacement_item_sku
  @replacement_item_sku
end

#return_item_actionObject

Returns the value of attribute return_item_action.



113
114
115
# File 'app/models/rma_item.rb', line 113

def return_item_action
  @return_item_action
end

#returned_item_descriptionObject

Returns the value of attribute returned_item_description.



113
114
115
# File 'app/models/rma_item.rb', line 113

def returned_item_description
  @returned_item_description
end

#returned_item_idObject (readonly)



94
# File 'app/models/rma_item.rb', line 94

validates :returned_item_id, :returned_item_quantity, :returned_reason, presence: true

#returned_item_quantityObject (readonly)



94
# File 'app/models/rma_item.rb', line 94

validates :returned_item_id, :returned_item_quantity, :returned_reason, presence: true

#returned_item_skuObject

Returns the value of attribute returned_item_sku.



113
114
115
# File 'app/models/rma_item.rb', line 113

def returned_item_sku
  @returned_item_sku
end

#returned_line_item_idObject (readonly)



99
100
# File 'app/models/rma_item.rb', line 99

validates :returned_line_item_id, presence: { if: ->(i) { i.rma&.original_invoice.present? },
message: 'must be present on original invoice. Remove link to original invoice if you still want to include it.' }

#returned_reasonObject (readonly)



94
# File 'app/models/rma_item.rb', line 94

validates :returned_item_id, :returned_item_quantity, :returned_reason, presence: true

Class Method Details

.activeActiveRecord::Relation<RmaItem>

A relation of RmaItems that are active. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



71
# File 'app/models/rma_item.rb', line 71

scope :active, -> { where.not(state: 'voided') }

.awaiting_inspectionActiveRecord::Relation<RmaItem>

A relation of RmaItems that are awaiting inspection. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



73
# File 'app/models/rma_item.rb', line 73

scope :awaiting_inspection, -> { where(state: 'awaiting_inspection') }

.calculate_credit_percentage(location, liable) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
# File 'app/models/rma_item.rb', line 186

def self.calculate_credit_percentage(location, liable)
  return nil if location.nil? || liable.nil?

  # under_warranty is not taken into account at this point.  a product could
  # still be under warranty but if the customer broke it they're still liable
  # it's not a fault of the product manufacturing
  if liable == 'WARMLYYOURS' || location == 'AVAILABLE'
    100
  else # customer is liable, but it's not resellable
    50
  end
end

.liable_select_optionsObject



199
200
201
# File 'app/models/rma_item.rb', line 199

def self.liable_select_options
  [%w[WarmlyYours WARMLYYOURS], %w[Customer CUSTOMER]]
end

.replacement_requiredActiveRecord::Relation<RmaItem>

A relation of RmaItems that are replacement required. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:

Validations:



75
# File 'app/models/rma_item.rb', line 75

scope :replacement_required, -> { where(replacement_required: true) }

.requestedActiveRecord::Relation<RmaItem>

A relation of RmaItems that are requested. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



72
# File 'app/models/rma_item.rb', line 72

scope :requested, -> { where(state: 'requested') }

.returnableActiveRecord::Relation<RmaItem>

A relation of RmaItems that are returnable. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



79
# File 'app/models/rma_item.rb', line 79

scope :returnable, -> { where(state: %w[requested awaiting_inspection]) }

.returnedActiveRecord::Relation<RmaItem>

A relation of RmaItems that are returned. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



74
# File 'app/models/rma_item.rb', line 74

scope :returned, -> { where(state: 'returned') }

.shipping_errorActiveRecord::Relation<RmaItem>

A relation of RmaItems that are shipping error. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



78
# File 'app/models/rma_item.rb', line 78

scope :shipping_error, -> { where(returned_reason: %w[LIT PMI]) }

.tech_examination_neededActiveRecord::Relation<RmaItem>

A relation of RmaItems that are tech examination needed. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



80
# File 'app/models/rma_item.rb', line 80

scope :tech_examination_needed, -> { where(tech_examination_needed: true) }

.will_be_returnedActiveRecord::Relation<RmaItem>

A relation of RmaItems that are will be returned. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



76
# File 'app/models/rma_item.rb', line 76

scope :will_be_returned, -> { where(will_not_be_returned: [false, nil]) }

.will_not_be_returnedActiveRecord::Relation<RmaItem>

A relation of RmaItems that are will not be returned. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



77
# File 'app/models/rma_item.rb', line 77

scope :will_not_be_returned, -> { where(will_not_be_returned: true) }

Instance Method Details

#auto_returnObject



350
351
352
353
354
355
# File 'app/models/rma_item.rb', line 350

def auto_return
  self.restocking_reason = returned_reason
  self.qty_to_receive = returned_item_quantity
  self.returned_item_location ||= 'SCRAP'
  returned!
end

#can_be_unreturned?Boolean

Returns:

  • (Boolean)


226
227
228
# File 'app/models/rma_item.rb', line 226

def can_be_unreturned?
  returned? && !rma.credit_in_process? && !rma.credited_partially_refunded? && !rma.credited_fully_refunded? && !rma.voided? && !credit_order_line_item&.resource&.printed?
end

#can_be_voided?Boolean

Returns:

  • (Boolean)


222
223
224
# File 'app/models/rma_item.rb', line 222

def can_be_voided?
  awaiting_inspection? || requested?
end

#credit_order_line_itemLineItem

Returns:

See Also:



64
# File 'app/models/rma_item.rb', line 64

has_one :credit_order_line_item, class_name: 'LineItem', foreign_key: 'credit_rma_item_id'

#credit_percentageObject



218
219
220
# File 'app/models/rma_item.rb', line 218

def credit_percentage
  RmaItem.calculate_credit_percentage(returned_item_location, liable)
end

#delete_linked_credit_order_line_itemObject



207
208
209
210
211
212
213
214
215
216
# File 'app/models/rma_item.rb', line 207

def delete_linked_credit_order_line_item
  return true if credit_order_line_item.nil?

  co = credit_order_line_item.resource
  credit_order_line_item.destroy
  co.reload
  co.returned
  co.destroy if co.line_items.non_shipping.count.zero?
  true
end

#editable?Boolean

Returns:

  • (Boolean)


203
204
205
# File 'app/models/rma_item.rb', line 203

def editable?
  awaiting_inspection? || requested? # Todo add a new state for received but not returned
end

#events_for_selectObject



373
374
375
# File 'app/models/rma_item.rb', line 373

def events_for_select
  [["#{human_state_name.titleize} (Current)", '']] + possible_events_for_select
end

#is_customer_fault?Boolean

Returns:

  • (Boolean)


275
276
277
# File 'app/models/rma_item.rb', line 275

def is_customer_fault?
  rma_reason_code.customer_fault?
end

#item_ledger_entriesActiveRecord::Relation<ItemLedgerEntry>

Returns:

See Also:



67
# File 'app/models/rma_item.rb', line 67

has_many :item_ledger_entries

#possible_eventsObject



365
366
367
# File 'app/models/rma_item.rb', line 365

def possible_events
  state_transitions.map(&:event).sort
end

#possible_events_for_selectObject



369
370
371
# File 'app/models/rma_item.rb', line 369

def possible_events_for_select
  possible_events.map { |evt| [evt.to_s.titleize, evt] }
end

#possible_serial_numbersObject



230
231
232
233
234
235
236
237
238
239
240
# File 'app/models/rma_item.rb', line 230

def possible_serial_numbers
  return [] unless returned_line_item

  sns = []
  if returned_line_item.require_reservation? && returned_line_item.serial_numbers.present?
    sns = returned_line_item.serial_numbers
  elsif (li_children = returned_line_item.children.to_a.select(&:require_reservation?)).present?
    sns = li_children.map(&:serial_numbers)
  end
  sns.to_a.flatten
end

#reactivate_serial_numbersObject



242
243
244
245
246
247
248
249
250
251
# File 'app/models/rma_item.rb', line 242

def reactivate_serial_numbers
  serial_numbers.each do |sn|
    if sn.store_item_id != store_item.id && !store_item.is_kit?
      sn.update_attribute('store_item_id',
                          store_item.id)
    end
    sn.trigger_available
    sn.reprint
  end
end

#reason_codes_for_select(include_advanced: true) ⇒ Object



357
358
359
360
361
362
363
# File 'app/models/rma_item.rb', line 357

def reason_codes_for_select(include_advanced: true)
  return [] unless returned_item

  returned_item.rma_reason_codes(include_advanced: include_advanced).order(:code).map do |e|
    [e.code_and_description, e.code, { 'data-default-location' => e.default_location }]
  end
end

#replacement_item_descriptionObject



283
284
285
# File 'app/models/rma_item.rb', line 283

def replacement_item_description
  replacement_item.try(:name)
end

#replacement_orderOrder

Returns:

See Also:



63
# File 'app/models/rma_item.rb', line 63

belongs_to :replacement_order, class_name: 'Order', inverse_of: :rma_items, optional: true

#replacement_order_line_itemLineItem

Returns:

See Also:



65
66
# File 'app/models/rma_item.rb', line 65

has_one :replacement_order_line_item, class_name: 'LineItem',
foreign_key: 'replacement_rma_item_id'

#returned_itemItem

Returns:

See Also:



61
# File 'app/models/rma_item.rb', line 61

belongs_to :returned_item, class_name: 'Item', optional: true

#returned_line_is_kit_component?Boolean

True when this return row is tied to an invoice line that is a child line (e.g. kit part).

Returns:

  • (Boolean)


378
379
380
# File 'app/models/rma_item.rb', line 378

def returned_line_is_kit_component?
  returned_line_item_id.present? && returned_line_item&.parent_id.present?
end

#returned_line_itemLineItem

Returns:

See Also:



62
# File 'app/models/rma_item.rb', line 62

belongs_to :returned_line_item, class_name: 'LineItem', optional: true

#rmaRma

Returns:

See Also:



56
# File 'app/models/rma_item.rb', line 56

belongs_to :rma, inverse_of: :rma_items, optional: true

#rma_reason_codeRmaReasonCode



57
58
# File 'app/models/rma_item.rb', line 57

belongs_to :rma_reason_code, foreign_key: 'returned_reason', primary_key: 'code',
optional: true

#rma_restocking_reason_codeRmaReasonCode



59
60
# File 'app/models/rma_item.rb', line 59

belongs_to :rma_restocking_reason_code, foreign_key: 'restocking_reason',
class_name: 'RmaReasonCode', primary_key: 'code', optional: true

#serial_numbersActiveRecord::Relation<SerialNumber>

Returns:

See Also:



69
# File 'app/models/rma_item.rb', line 69

has_and_belongs_to_many :serial_numbers

#set_default_return_locationObject



382
383
384
385
# File 'app/models/rma_item.rb', line 382

def set_default_return_location
  self.returned_item_location = rma_reason_code&.default_location if returned_reason_changed? || returned_item_location.blank?
  self.liable ||= 'WARMLYYOURS' # a default TODO leverage reason codes
end

#store_itemObject



287
288
289
290
# File 'app/models/rma_item.rb', line 287

def store_item
  store = rma.return_shipping_address.warehouse_store
  returned_item.store_item_for(store.id, returned_item_location)
end

#update_item_ledgerObject



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'app/models/rma_item.rb', line 292

def update_item_ledger
  store = rma.return_shipping_address.warehouse_store
  if returned_item.is_kit?
    kit_serial_number_ids = serial_number_ids.dup
    # Use returned line item children to get the actual ordered kit line item quantities
    if returned_line_item.present?
      returned_line_item.children.each do |kit_li|
        ki_store_item = kit_li.item.store_item_for(store.id, 'AVAILABLE')
        ki_serial_number_ids = serial_numbers.select do |sn|
          sn.store_item_id == ki_store_item.id
        end.map(&:id)
        kit_li_quantity = kit_li.quantity * returned_item_quantity / returned_line_item.quantity
        ItemLedgerEntry.create!(category: 'RMA_RECEIPT', store: store, item: kit_li.item,
                                gl_date: Date.current, item_kit: returned_item, quantity: kit_li_quantity, currency: store.currency, unit_cost: ki_store_item.unit_cogs, total_cost: ki_store_item.unit_cogs * kit_li_quantity, rma_item: self, location: returned_item_location, description: "RMA ##{rma.rma_number} #{returned_item.sku} (#{kit_li.sku})", serial_number_ids: ki_serial_number_ids)
        kit_serial_number_ids -= ki_serial_number_ids
      end
    else
      kit_contents = ItemRelation.where(source_item_id: returned_item)
      kit_contents.each do |kit_content|
        kc_store_item = kit_content.target_item.store_item_for(store.id, 'AVAILABLE')
        kc_serial_number_ids = serial_numbers.select do |sn|
          sn.store_item_id == kc_store_item.id
        end.map(&:id)
        kc_quantity = kit_content.quantity * returned_item_quantity
        ItemLedgerEntry.create!(category: 'RMA_RECEIPT', store: store, item: kit_content.target_item,
          gl_date: Date.current, item_kit: returned_item, quantity: kc_quantity, currency: store.currency, unit_cost: kc_store_item.unit_cogs, total_cost: kc_store_item.unit_cogs * kc_quantity, rma_item: self, location: returned_item_location, description: "RMA ##{rma.rma_number} #{returned_item.sku} (#{kit_content.target_item.sku})", serial_number_ids: kc_serial_number_ids)
        kit_serial_number_ids -= kc_serial_number_ids
      end
    end

    ItemLedgerEntry.create!(store: store,
                            item: returned_item,
                            category: 'RMA_RECEIPT_KIT',
                            gl_date: Date.current,
                            quantity: 0,
                            quantity_eval: -returned_item_quantity,
                            currency: store.currency,
                            unit_cost: 0,
                            total_cost: 0,
                            location: returned_item_location,
                            rma_item: self,
                            serial_number_ids: kit_serial_number_ids)
  else
    si = returned_item.store_item_for(store.id, 'AVAILABLE')

    ItemLedgerEntry.create!(category: 'RMA_RECEIPT',
                            store: store, item: returned_item,
                            gl_date: Date.current,
                            quantity: returned_item_quantity,
                            currency: store.currency,
                            unit_cost: si.unit_cogs,
                            total_cost: si.unit_cogs * returned_item_quantity,
                            rma_item: self, location: returned_item_location,
                            description: "RMA ##{rma.rma_number} #{returned_item.sku}",
                            serial_number_ids: serial_number_ids)
  end
end