Class: SpreeCmCommissioner::InventoryItems::Reset

Inherits:
Object
  • Object
show all
Extended by:
ServiceModuleThrowable
Includes:
Spree::ServiceModule::Base
Defined in:
app/services/spree_cm_commissioner/inventory_items/reset.rb

Instance Method Summary collapse

Methods included from ServiceModuleThrowable

call!

Instance Method Details

#call(inventory_item:) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'app/services/spree_cm_commissioner/inventory_items/reset.rb', line 7

def call(inventory_item:)
  max_capacity = variant_total_inventory(inventory_item)
  total_purchases = variant_total_purchases(inventory_item)
  # Everything an operator has taken off public sale, by either mechanism: one ReservedBlock per
  # seat for seat-selection products, and quantity_locked for everything else. Both must be
  # subtracted or a reset hands withheld units straight back to the public.
  #
  # locked_count is self-healing because the rows ARE the record of what was deducted.
  # quantity_locked is not — it is a counter with no rows behind it, so a reset preserves it
  # rather than recomputing it. It is deliberately left out of the update below.
  total_locked = inventory_item.locked_count + inventory_item.quantity_locked
  quantity_available = [max_capacity - total_purchases - total_locked, 0].max
  quantity_on_hold, quantity_locked_on_hold = variant_total_on_hold(inventory_item)

  updated = inventory_item.update(
    max_capacity: max_capacity,
    quantity_available: quantity_available,
    quantity_on_hold: quantity_on_hold,
    quantity_locked_on_hold: quantity_locked_on_hold
  )
  return failure(nil, :failed_to_update_inventory_item) unless updated

  reset_redis_inventory(inventory_item)

  success(nil)
end

#variant_total_inventory(inventory_item) ⇒ Object



34
35
36
37
38
39
40
41
42
# File 'app/services/spree_cm_commissioner/inventory_items/reset.rb', line 34

def variant_total_inventory(inventory_item)
  # for shipment, total_on_hand is not orignal stock. shipment does subtract the stock.
  # to get desire result, we need to add to total_purchase.
  if inventory_item.variant.delivery_required?
    inventory_item.variant.total_on_hand.to_i + variant_total_purchases(inventory_item)
  else
    inventory_item.variant.total_on_hand.to_i
  end
end

#variant_total_on_hold(inventory_item) ⇒ Object

[public_on_hold, locked_on_hold] — SPLIT BY POOL.

A locked-stock line holds against the locked counter, so counting it here would inflate the public one and under-report public availability: exactly the drift this service exists to correct.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'app/services/spree_cm_commissioner/inventory_items/reset.rb', line 59

def variant_total_on_hold(inventory_item)
  active_hold_order_ids = SpreeCmCommissioner::InventoryHold.active_or_payment_locked.pluck(:order_id)
  return [0, 0] if active_hold_order_ids.empty?

  scope = Spree::LineItem
          .where(order_id: active_hold_order_ids)
          .joins(variant: :inventory_items)
          .where(cm_inventory_items: { id: inventory_item.id })

  if inventory_item.permanent_stock?
    scope = scope.where('? BETWEEN spree_line_items.from_date AND spree_line_items.to_date', inventory_item.inventory_date)
  end

  # Partitioned in Ruby, not SQL. StoreMetadata's coder does JSON.dump on the whole hash, so the
  # jsonb column holds a JSON *string* rather than an object — `private_metadata ->> 'locked_stock'`
  # returns NULL for every row. Reset is a maintenance pass over one inventory item, so loading
  # its held lines is cheap.
  totals = { false => 0, true => 0 }
  scope.find_each { |line_item| totals[line_item.locked_stock?] += line_item.quantity }

  [totals[false], totals[true]]
end

#variant_total_purchases(inventory_item) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'app/services/spree_cm_commissioner/inventory_items/reset.rb', line 44

def variant_total_purchases(inventory_item)
  scope = inventory_item.variant.line_items.affecting_inventory

  if inventory_item.permanent_stock?
    scope.where('? BETWEEN from_date AND to_date', inventory_item.inventory_date).sum(:quantity).to_i
  else
    scope.sum(:quantity).to_i
  end
end