Module: Models::Reservable
- Extended by:
- ActiveSupport::Concern
- Included in:
- LineItem
- Defined in:
- app/concerns/models/reservable.rb
Overview
Serial number reservation behaviour for line-item-like models.
Mixed into line items whose items can reserve individual serial numbers
against the quantity being sold or shipped. Handles auto-reservation on
create/update, committing reservations, and linking/unlinking serial
numbers at shipment time.
Has many collapse
-
#reserved_serial_numbers ⇒ ActiveRecord::Relation<ReservedSerialNumber>
Serial number reservations made for this record.
Has and belongs to many collapse
-
#serial_numbers ⇒ ActiveRecord::Relation<SerialNumber>
Serial numbers linked (shipped) to this record.
Instance Method Summary collapse
-
#all_reserved_serial_numbers ⇒ Array<ReservedSerialNumber>
Reserved serial numbers on this record plus all of its children.
-
#all_reserved_serial_numbers_available? ⇒ Boolean
Whether every reservation fits within its serial number's availability.
-
#auto_reserve_serial_numbers ⇒ Boolean
Automatically reserves serial numbers for any unreserved quantity.
-
#available_serial_numbers ⇒ Array<SerialNumber>
Serial numbers available for reservation, least-available-quantity first.
-
#commit_reserved_serial_numbers ⇒ Array<ReservedSerialNumber>
Marks all reserved serial numbers as committed.
-
#fully_reserved? ⇒ Boolean
Whether the reserved quantity covers the full line item quantity.
-
#kit_components_requires_reservation? ⇒ Boolean
Whether the item is a kit with components that require reservation.
-
#link_serial_numbers ⇒ Array<ReservedSerialNumber>
Links each reserved serial number to this record and marks it shipped.
-
#rejoin_serial_numbers ⇒ Array<ReservedSerialNumber>
Rejoins each reserved serial number back to its original.
-
#require_reservation? ⇒ Boolean
Whether this line item requires serial number reservation.
-
#reservable_item ⇒ Item
Item whose serial numbers may be reserved.
-
#reservable_store_item ⇒ StoreItem
Store item whose serial number pool is used for reservation.
-
#show_serial_number_toggle? ⇒ Boolean
Whether the serial number toggle should be shown in the UI.
-
#skip_reservation_screen? ⇒ Boolean
Whether the serial number reservation screen can be skipped.
-
#split_serial_numbers ⇒ Array<ReservedSerialNumber>
Splits each reserved serial number.
-
#total_reserved_serial_numbers ⇒ Integer
Total quantity covered by non-destroyed serial number reservations.
-
#uncommit_reserved_serial_numbers ⇒ Array<ReservedSerialNumber>
Marks all reserved serial numbers as uncommitted.
-
#unlink_serial_numbers ⇒ Array<SerialNumber>
Unlinks all serial numbers and marks them available again.
-
#update_serial_numbers_shipped_count ⇒ Array<SerialNumber>
Increments each linked serial number's shipped count by this quantity.
Instance Method Details
#all_reserved_serial_numbers ⇒ Array<ReservedSerialNumber>
Reserved serial numbers on this record plus all of its children.
133 134 135 136 137 |
# File 'app/concerns/models/reservable.rb', line 133 def all_reserved_serial_numbers res = reserved_serial_numbers.to_a children.each { |c| res = res.concat(c.reserved_serial_numbers) } res end |
#all_reserved_serial_numbers_available? ⇒ Boolean
Whether every reservation fits within its serial number's availability.
97 98 99 |
# File 'app/concerns/models/reservable.rb', line 97 def all_reserved_serial_numbers_available? reserved_serial_numbers.all? { |rsn| rsn.serial_number.qty_available >= rsn.qty } end |
#auto_reserve_serial_numbers ⇒ Boolean
Automatically reserves serial numbers for any unreserved quantity.
Picks the tightest-fitting available serial number (least available
quantity, then least unreserved, then least shipped) and creates a
reservation for the outstanding quantity.
171 172 173 174 175 176 177 178 |
# File 'app/concerns/models/reservable.rb', line 171 def auto_reserve_serial_numbers qty_to_reserve = quantity - total_reserved_serial_numbers if qty_to_reserve > 0 matching_serial_number = available_serial_numbers.select { |sn| sn.qty_available >= qty_to_reserve }.min_by { |sn| [sn.qty_available, -sn.qty_unreserved, sn.shipped_count || 0] } # make this into some clever SQL reserved_serial_numbers.create(serial_number: matching_serial_number, qty: qty_to_reserve) if matching_serial_number.present? end true end |
#available_serial_numbers ⇒ Array<SerialNumber>
Serial numbers available for reservation, least-available-quantity first.
75 76 77 |
# File 'app/concerns/models/reservable.rb', line 75 def available_serial_numbers reservable_store_item.serial_numbers.available.sort_by { |sn| [sn.qty_available, sn.number] } end |
#commit_reserved_serial_numbers ⇒ Array<ReservedSerialNumber>
Marks all reserved serial numbers as committed.
104 105 106 |
# File 'app/concerns/models/reservable.rb', line 104 def commit_reserved_serial_numbers update_reserved_serial_numbers_committed(true) end |
#fully_reserved? ⇒ Boolean
Whether the reserved quantity covers the full line item quantity.
89 90 91 |
# File 'app/concerns/models/reservable.rb', line 89 def fully_reserved? total_reserved_serial_numbers == quantity end |
#kit_components_requires_reservation? ⇒ Boolean
Whether the item is a kit with components that require reservation.
57 58 59 |
# File 'app/concerns/models/reservable.rb', line 57 def kit_components_requires_reservation? reservable_item.is_kit? && reservable_item.kit_components.any?(&:require_reservation?) end |
#link_serial_numbers ⇒ Array<ReservedSerialNumber>
Links each reserved serial number to this record and marks it shipped.
142 143 144 145 146 147 |
# File 'app/concerns/models/reservable.rb', line 142 def link_serial_numbers reserved_serial_numbers.each do |rsn| serial_numbers << rsn.serial_number rsn.serial_number.trigger_shipped end end |
#rejoin_serial_numbers ⇒ Array<ReservedSerialNumber>
Rejoins each reserved serial number back to its original.
125 126 127 128 |
# File 'app/concerns/models/reservable.rb', line 125 def rejoin_serial_numbers # reload here after each one or else the total on the original gets messed up reserved_serial_numbers.each { |rsn| rsn.reload.rejoin_serial_number } end |
#require_reservation? ⇒ Boolean
Whether this line item requires serial number reservation.
65 66 67 68 69 |
# File 'app/concerns/models/reservable.rb', line 65 def require_reservation? resource.respond_to?(:can_auto_reserve_serial_numbers?) && resource.can_auto_reserve_serial_numbers? && reservable_item&.require_reservation? end |
#reservable_item ⇒ Item
Item whose serial numbers may be reserved.
34 35 36 |
# File 'app/concerns/models/reservable.rb', line 34 def reservable_item item end |
#reservable_store_item ⇒ StoreItem
Store item whose serial number pool is used for reservation.
41 42 43 |
# File 'app/concerns/models/reservable.rb', line 41 def reservable_store_item store_item end |
#reserved_serial_numbers ⇒ ActiveRecord::Relation<ReservedSerialNumber>
Returns serial number
reservations made for this record.
17 |
# File 'app/concerns/models/reservable.rb', line 17 has_many :reserved_serial_numbers, dependent: :destroy |
#serial_numbers ⇒ ActiveRecord::Relation<SerialNumber>
Returns serial numbers linked
(shipped) to this record.
21 |
# File 'app/concerns/models/reservable.rb', line 21 has_and_belongs_to_many :serial_numbers |
#show_serial_number_toggle? ⇒ Boolean
Whether the serial number toggle should be shown in the UI.
184 185 186 |
# File 'app/concerns/models/reservable.rb', line 184 def show_serial_number_toggle? require_reservation? or serial_numbers.present? or reserved_serial_numbers.present? end |
#skip_reservation_screen? ⇒ Boolean
Whether the serial number reservation screen can be skipped.
49 50 51 |
# File 'app/concerns/models/reservable.rb', line 49 def skip_reservation_screen? fully_reserved? && reservable_item.skip_serial_number_reservation_screen? end |
#split_serial_numbers ⇒ Array<ReservedSerialNumber>
Splits each reserved serial number.
118 119 120 |
# File 'app/concerns/models/reservable.rb', line 118 def split_serial_numbers reserved_serial_numbers.each { |rsn| rsn.reload.split_serial_number } end |
#total_reserved_serial_numbers ⇒ Integer
Total quantity covered by non-destroyed serial number reservations.
82 83 84 |
# File 'app/concerns/models/reservable.rb', line 82 def total_reserved_serial_numbers reserved_serial_numbers.reject(&:marked_for_destruction?).sum { |rsn| rsn.qty || 0 } end |
#uncommit_reserved_serial_numbers ⇒ Array<ReservedSerialNumber>
Marks all reserved serial numbers as uncommitted.
111 112 113 |
# File 'app/concerns/models/reservable.rb', line 111 def uncommit_reserved_serial_numbers update_reserved_serial_numbers_committed(false) end |
#unlink_serial_numbers ⇒ Array<SerialNumber>
Unlinks all serial numbers and marks them available again.
152 153 154 155 |
# File 'app/concerns/models/reservable.rb', line 152 def unlink_serial_numbers serial_numbers.each(&:trigger_available) serial_numbers.clear end |
#update_serial_numbers_shipped_count ⇒ Array<SerialNumber>
Increments each linked serial number's shipped count by this quantity.
160 161 162 |
# File 'app/concerns/models/reservable.rb', line 160 def update_serial_numbers_shipped_count serial_numbers.each { |sn| sn.update_attribute!(:shipped_count, (sn.shipped_count || 0) + quantity) } end |