Class: RmaItem

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable, Models::Lineage, 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 =

Available liable options.

%w[WARMLYYOURS CUSTOMER].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

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

#ancestors, #ancestors_ids, #children_and_roots, #descendants, #descendants_ids, #ensure_non_recursive_lineage, #family_members, #generate_full_name, #generate_full_name_array, #lineage, #lineage_array, #lineage_simple, #root, #root_id, #self_ancestors_and_descendants, #self_ancestors_and_descendants_ids, #self_and_ancestors, #self_and_ancestors_ids, #self_and_children, #self_and_descendants, #self_and_descendants_ids, #self_and_siblings, #self_and_siblings_ids, #siblings, #siblings_ids

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

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#max_returnable_quantityObject

Upper bound for the Quantity dropdown on the add-items form. Defaults to the
full invoiced quantity (original_qty); the add-items view overrides it with
the quantity still returnable after other active RMAs claim their share.



326
327
328
329
330
# File 'app/models/rma_item.rb', line 326

def max_returnable_quantity
  return @max_returnable_quantity if instance_variable_defined?(:@max_returnable_quantity) && @max_returnable_quantity.present?

  original_qty
end

#new_recordObject

Returns the value of attribute new_record.



153
154
155
# File 'app/models/rma_item.rb', line 153

def new_record
  @new_record
end

#non_resalable_qtyObject (readonly)



121
122
123
124
# File 'app/models/rma_item.rb', line 121

validates :non_resalable_qty,
            numericality: { greater_than_or_equal_to: 0,
less_than_or_equal_to: ->(i) { i.returned_item_quantity || 0 },
only_integer: true, allow_nil: true }

#not_returned_qtyObject (readonly)



130
131
132
133
# File 'app/models/rma_item.rb', line 130

validates :not_returned_qty,
            numericality: { greater_than_or_equal_to: 0,
less_than_or_equal_to: ->(i) { i.returned_item_quantity || 0 },
only_integer: true, allow_nil: true }

#original_qtyObject



332
333
334
335
336
337
338
339
340
341
342
# File 'app/models/rma_item.rb', line 332

def original_qty
  return @original_qty if instance_variable_defined?(:@original_qty) && @original_qty.present?

  if returned_line_item.present? && returned_item.present? && (returned_line_item.item != returned_item) && returned_line_item.item.is_kit?
    relations = returned_line_item.item.kit_target_item_relations
    relation = relations.loaded? ? relations.find { |r| r.target_item_id == returned_item.id } : relations.where(target_item_id: returned_item.id).first
    returned_line_item.quantity * relation&.quantity.to_i
  else
    returned_line_item.try(:quantity)
  end
end

Returns the value of attribute print_label.



153
154
155
# File 'app/models/rma_item.rb', line 153

def print_label
  @print_label
end

#qty_to_receiveObject

Returns the value of attribute qty_to_receive.



153
154
155
# File 'app/models/rma_item.rb', line 153

def qty_to_receive
  @qty_to_receive
end

#return_item_actionObject

Returns the value of attribute return_item_action.



153
154
155
# File 'app/models/rma_item.rb', line 153

def return_item_action
  @return_item_action
end

#returned_item_descriptionObject



317
318
319
320
321
# File 'app/models/rma_item.rb', line 317

def returned_item_description
  return @returned_item_description if instance_variable_defined?(:@returned_item_description) && @returned_item_description.present?

  returned_item.try(:name)
end

#returned_item_idObject (readonly)



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

validates :returned_item_id, :returned_item_quantity, presence: true

#returned_item_quantityObject (readonly)



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

validates :returned_item_id, :returned_item_quantity, presence: true

#returned_line_item_idObject (readonly)



117
118
# File 'app/models/rma_item.rb', line 117

validates :returned_line_item_id, presence: { if: ->(i) { !i.child? && 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)



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

validates :returned_reason, presence: true, unless: :child?

Class Method Details

.activeActiveRecord::Relation<RmaItem>

A relation of RmaItems that are active. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



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

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:



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

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

.calculate_credit_percentage(location, liable) ⇒ Object



245
246
247
248
249
250
251
252
253
254
255
256
# File 'app/models/rma_item.rb', line 245

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

.children_onlyActiveRecord::Relation<RmaItem>

A relation of RmaItems that are children only. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



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

scope :children_only, -> { where.not(parent_id: nil) }

.liable_select_optionsObject



258
259
260
# File 'app/models/rma_item.rb', line 258

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

.parents_onlyActiveRecord::Relation<RmaItem>

A relation of RmaItems that are parents only. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



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

scope :parents_only, -> { where(parent_id: nil) }

.rejectedActiveRecord::Relation<RmaItem>

A relation of RmaItems that are rejected. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



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

scope :rejected, -> { where(non_resalable: true).or(where(not_returned: true)) }

.replacement_requiredActiveRecord::Relation<RmaItem>

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

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:

Validations (unless => #child? ):



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

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:



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

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

.returnableActiveRecord::Relation<RmaItem>

A relation of RmaItems that are returnable. Active Record Scope

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



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

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:



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

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:



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

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:



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

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:



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

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:



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

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

Instance Method Details

#auto_returnObject



480
481
482
483
484
485
# File 'app/models/rma_item.rb', line 480

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)


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

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)


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

def can_be_voided?
  awaiting_inspection? || requested?
end

#child?Boolean

Returns:

  • (Boolean)


517
518
519
# File 'app/models/rma_item.rb', line 517

def child?
  parent_id.present?
end

#credit_order_line_itemLineItem

Returns:

See Also:



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

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

#credit_percentageObject



278
279
280
# File 'app/models/rma_item.rb', line 278

def credit_percentage
  RmaItem.calculate_credit_percentage(returned_item_location, liable)
end

#creditable_quantity(qty_received) ⇒ Object

Resalable (credit-eligible) units within a received quantity: the received qty
minus its non-resalable (damaged) portion. Damaged units are booked to inventory
(at non_resalable_location) but excluded from the customer credit, mirroring how
kit components are credited (good units only). Returns 0 when the whole received
qty is damaged.



569
570
571
572
573
574
575
# File 'app/models/rma_item.rb', line 569

def creditable_quantity(qty_received)
  qty_received = qty_received.to_i
  return qty_received unless non_resalable?

  damaged = non_resalable_qty.positive? ? [non_resalable_qty, qty_received].min : qty_received
  qty_received - damaged
end

#damaged_return_locationObject

The inventory location the non-resalable (damaged) units are booked into during
RMA receipt, for both normal items and kit components. Operator-selectable on the
receive-items form (stored in non_resalable_location); falls back to SCRAP when
left blank (the historical default). returned_item_location stays the good-units
location: a normal item's good remainder and a kit component's good units (booked
at the kit's location) both go there.



546
547
548
# File 'app/models/rma_item.rb', line 546

def damaged_return_location
  non_resalable_location.presence || 'SCRAP'
end

#delete_linked_credit_order_line_itemObject



266
267
268
269
270
271
272
273
274
275
276
# File 'app/models/rma_item.rb', line 266

def delete_linked_credit_order_line_item
  credit_lines = LineItem.where(credit_rma_item_id: id)
  return true if credit_lines.none?

  co = credit_lines.first.resource
  credit_lines.destroy_all
  co.reload
  co.returned
  co.destroy if co.line_items.non_shipping.none?
  true
end

#editable?Boolean

Returns:

  • (Boolean)


262
263
264
# File 'app/models/rma_item.rb', line 262

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

#events_for_selectObject



503
504
505
# File 'app/models/rma_item.rb', line 503

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

#expand_kit_components!Object

Creates child rma_items for each kit component. Called after saving a kit
parent rma_item from add_items or bulk_add_items.



579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'app/models/rma_item.rb', line 579

def expand_kit_components!
  return unless returned_item&.is_kit?
  return if children.any?

  if returned_line_item.present? && returned_line_item.has_children?
    returned_line_item.children.each do |component_line|
      adjusted_qty = [component_line.quantity * returned_item_quantity / returned_line_item.quantity, 1].max
      children.create!(
        rma: rma,
        returned_item: component_line.item,
        returned_line_item: component_line,
        returned_item_quantity: adjusted_qty,
        returned_reason: returned_reason,
        non_resalable: false,
        non_resalable_qty: 0,
        not_returned: false,
        not_returned_qty: 0
      )
    end
  else
    returned_item.kit_target_item_relations.includes(:target_item).each do |relation|
      children.create!(
        rma: rma,
        returned_item: relation.target_item,
        returned_item_quantity: relation.quantity * returned_item_quantity,
        returned_reason: returned_reason,
        non_resalable: false,
        non_resalable_qty: 0,
        not_returned: false,
        not_returned_qty: 0
      )
    end
  end
end

#good_quantityObject

For kit component children: how many units are in good (resalable, credit-eligible) condition.
Subtracts both non-resalable (received but damaged) and not-returned (never received) units.
When a flag is set but qty is 0 (e.g. blank TomSelect submission), all units are assumed affected.



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
# File 'app/models/rma_item.rb', line 524

def good_quantity
  return returned_item_quantity unless child?

  damaged = if non_resalable?
              non_resalable_qty.positive? ? non_resalable_qty : returned_item_quantity
            else
              0
            end
  missing = if not_returned?
              not_returned_qty.positive? ? not_returned_qty : returned_item_quantity
            else
              0
            end
  [returned_item_quantity - damaged - missing, 0].max
end

#is_customer_fault?Boolean

Returns:

  • (Boolean)


344
345
346
# File 'app/models/rma_item.rb', line 344

def is_customer_fault?
  rma_reason_code.customer_fault?
end

#item_ledger_entriesActiveRecord::Relation<ItemLedgerEntry>

Returns:

See Also:



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

has_many :item_ledger_entries

#non_resalable_quantityObject

Quantity of this item's units flagged non-resalable (damaged). A set flag with a
zero qty (blank TomSelect submission) means the whole line is damaged.



552
553
554
555
556
# File 'app/models/rma_item.rb', line 552

def non_resalable_quantity
  return 0 unless non_resalable?

  non_resalable_qty.positive? ? non_resalable_qty : returned_item_quantity
end

#possible_eventsObject



495
496
497
# File 'app/models/rma_item.rb', line 495

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

#possible_events_for_selectObject



499
500
501
# File 'app/models/rma_item.rb', line 499

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

#possible_serial_numbersObject



290
291
292
293
294
295
296
297
298
299
300
# File 'app/models/rma_item.rb', line 290

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



302
303
304
305
306
307
308
309
310
311
# File 'app/models/rma_item.rb', line 302

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



487
488
489
490
491
492
493
# File 'app/models/rma_item.rb', line 487

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



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

def replacement_item_description
  replacement_item.try(:name)
end

#replacement_item_skuObject



348
349
350
# File 'app/models/rma_item.rb', line 348

def replacement_item_sku
  replacement_item.try(:sku)
end

#replacement_orderOrder

Returns:

See Also:



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

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

#replacement_order_line_itemLineItem

Returns:

See Also:



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

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

#resalable_quantityObject

Units of this line that are resalable (not flagged non-resalable). Zero when the
whole line is non-resalable — in which case there is no resalable return location.



560
561
562
# File 'app/models/rma_item.rb', line 560

def resalable_quantity
  returned_item_quantity - non_resalable_quantity
end

#returned_itemItem

Returns:

See Also:



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

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

#returned_item_skuObject



313
314
315
# File 'app/models/rma_item.rb', line 313

def returned_item_sku
  returned_item.try(:sku)
end

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


508
509
510
# File 'app/models/rma_item.rb', line 508

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

#returned_line_itemLineItem

Returns:

See Also:



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

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

#rmaRma

Returns:

See Also:



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

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

#rma_reason_codeRmaReasonCode



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

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

#rma_restocking_reason_codeRmaReasonCode



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

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:



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

has_and_belongs_to_many :serial_numbers

#set_default_return_locationObject



512
513
514
515
# File 'app/models/rma_item.rb', line 512

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



356
357
358
359
# File 'app/models/rma_item.rb', line 356

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

#update_item_ledgerObject



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'app/models/rma_item.rb', line 361

def update_item_ledger
  store = rma.return_shipping_address.warehouse_store
  if returned_item.is_kit? && children.any?
    kit_serial_number_ids = serial_number_ids.dup
    children.each do |component|
      ki_store_item = component.returned_item.store_item_for(store.id, 'AVAILABLE')
      raise 'Unable to find store item for returned item' if ki_store_item.nil?

      ki_serial_number_ids = serial_numbers.select { |sn| sn.store_item_id == ki_store_item.id }.map(&:id)
      good_qty = component.good_quantity
      damaged_qty = component.returned_item_quantity - good_qty
      desc = "RMA ##{rma.rma_number} #{returned_item.sku} (#{component.returned_item.sku})"

      if good_qty.positive?
        ItemLedgerEntry.create!(
          category: 'RMA_RECEIPT', store: store,
          item: component.returned_item, item_kit: returned_item,
          gl_date: Date.current,
          quantity: good_qty,
          currency: store.currency,
          unit_cost: ki_store_item.unit_cogs,
          total_cost: ki_store_item.unit_cogs * good_qty,
          rma_item: self, location: returned_item_location,
          description: desc,
          serial_number_ids: ki_serial_number_ids
        )
      end

      if damaged_qty.positive?
        ItemLedgerEntry.create!(
          category: 'RMA_RECEIPT', store: store,
          item: component.returned_item, item_kit: returned_item,
          gl_date: Date.current,
          quantity: damaged_qty,
          currency: store.currency,
          unit_cost: ki_store_item.unit_cogs,
          total_cost: ki_store_item.unit_cogs * damaged_qty,
          rma_item: self, location: component.damaged_return_location,
          description: desc,
          serial_number_ids: []
        )
      end

      kit_serial_number_ids -= ki_serial_number_ids
    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
    )
  elsif returned_item.is_kit?
    kit_serial_number_ids = serial_number_ids.dup
    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 { |sn| sn.store_item_id == ki_store_item.id }.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 { |sn| sn.store_item_id == kc_store_item.id }.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')
    desc = "RMA ##{rma.rma_number} #{returned_item.sku}"
    # A non-resalable normal item splits its units: the damaged qty is booked to
    # the chosen non_resalable_location, the good remainder to returned_item_location.
    damaged_qty = non_resalable_quantity
    good_qty = returned_item_quantity - damaged_qty

    if good_qty.positive?
      ItemLedgerEntry.create!(category: 'RMA_RECEIPT',
                              store: store, item: returned_item,
                              gl_date: Date.current,
                              quantity: good_qty,
                              currency: store.currency,
                              unit_cost: si.unit_cogs,
                              total_cost: si.unit_cogs * good_qty,
                              rma_item: self, location: returned_item_location,
                              description: desc,
                              serial_number_ids: serial_number_ids)
    end

    if damaged_qty.positive?
      ItemLedgerEntry.create!(category: 'RMA_RECEIPT',
                              store: store, item: returned_item,
                              gl_date: Date.current,
                              quantity: damaged_qty,
                              currency: store.currency,
                              unit_cost: si.unit_cogs,
                              total_cost: si.unit_cogs * damaged_qty,
                              rma_item: self, location: damaged_return_location,
                              description: desc,
                              serial_number_ids: good_qty.positive? ? [] : serial_number_ids)
    end
  end
end

#uploadsActiveRecord::Relation<Upload>

Returns:

  • (ActiveRecord::Relation<Upload>)

See Also:



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

has_many :uploads, as: :resource, dependent: :destroy