Module: LineItemExtension
- Defined in:
- app/models/line_item_extension.rb
Overview
Association extension for has_many :line_items collections (mixed in
wherever the Models::Pickable concern declares line_items).
Adds collection-level helpers that the rest of the ERP relies on
without paying an N+1: heating-system aggregations
(#primary_heating_system_product_line, #primary_heating_element_spacing),
active-line filtering (#active_lines, #active_parent_lines,
#active_goods_lines, #active_services_lines, #active_shipping_lines),
preloads for the coupon engine (#with_discount_preloads), and a
stable MD5 #signature used to detect "is this packing list still
the same one we already shipped?" for shipment matching.
Instance Method Summary collapse
- #active_goods_lines ⇒ Object
- #active_lines ⇒ Object
- #active_lines_for_packaging ⇒ Object
- #active_lines_without_kit_parents ⇒ Object
- #active_non_shipping_lines ⇒ Object
- #active_parent_lines ⇒ Object
- #active_services_lines ⇒ Object
- #active_shipping_lines ⇒ Object
- #active_tax_unclassed_lines ⇒ Object
- #any_require_cable_spacing? ⇒ Boolean
- #changed_for_shipping? ⇒ Boolean
- #goods_changed? ⇒ Boolean
- #grouped_by_category ⇒ Object
- #has_goods_lines? ⇒ Boolean
- #has_goods_without_shipping? ⇒ Boolean
- #has_shipping_line? ⇒ Boolean
- #has_underlayment? ⇒ Boolean
- #lines_changed ⇒ Object
- #lines_changed? ⇒ Boolean
- #primary_heating_element_spacing ⇒ Object
- #primary_heating_system_product_line ⇒ Object
- #primary_heating_system_voltage ⇒ Object
- #signature ⇒ Object
- #ungrouped ⇒ Object
- #version ⇒ Object
-
#with_discount_preloads ⇒ Object
Preload associations commonly needed for coupon qualification and discount calculations.
Instance Method Details
#active_goods_lines ⇒ Object
128 129 130 |
# File 'app/models/line_item_extension.rb', line 128 def active_goods_lines active_lines.select(&:is_goods?) end |
#active_lines ⇒ Object
79 80 81 82 |
# File 'app/models/line_item_extension.rb', line 79 def active_lines # uniq is a safety in case multiple of the same lines are added to the collection to_a.reject { |li| li.destroyed? or li.marked_for_destruction? or li.frozen? }.uniq end |
#active_lines_for_packaging ⇒ Object
102 103 104 105 106 107 108 109 110 |
# File 'app/models/line_item_extension.rb', line 102 def active_lines_for_packaging active_lines.select do |li| li.is_goods? && ( (li.children_count.to_i.zero? && !li.parent&.catalog_item&.pack_at_kit_level&.to_b) || li.catalog_item.pack_at_kit_level.to_b ) end end |
#active_lines_without_kit_parents ⇒ Object
98 99 100 |
# File 'app/models/line_item_extension.rb', line 98 def active_lines_without_kit_parents active_lines.reject { |li| li.children_count.present? and li.children_count > 0 } end |
#active_non_shipping_lines ⇒ Object
120 121 122 |
# File 'app/models/line_item_extension.rb', line 120 def active_non_shipping_lines active_lines.reject(&:is_shipping?) end |
#active_parent_lines ⇒ Object
84 85 86 |
# File 'app/models/line_item_extension.rb', line 84 def active_parent_lines active_lines.reject { |li| li.parent_id.present? } end |
#active_services_lines ⇒ Object
132 133 134 |
# File 'app/models/line_item_extension.rb', line 132 def active_services_lines active_lines.select(&:is_service?) end |
#active_shipping_lines ⇒ Object
124 125 126 |
# File 'app/models/line_item_extension.rb', line 124 def active_shipping_lines active_lines.select(&:is_shipping?) end |
#active_tax_unclassed_lines ⇒ Object
136 137 138 |
# File 'app/models/line_item_extension.rb', line 136 def active_tax_unclassed_lines active_lines.select(&:is_tax_unclassed?) end |
#any_require_cable_spacing? ⇒ Boolean
58 59 60 61 |
# File 'app/models/line_item_extension.rb', line 58 def any_require_cable_spacing? # see if ant line items require cable spacing heating_elements.any? { |li| li.item&.is_cable_system? } end |
#changed_for_shipping? ⇒ Boolean
75 76 77 |
# File 'app/models/line_item_extension.rb', line 75 def changed_for_shipping? (active_goods_lines + active_services_lines).any? { |li| li.destroyed? or li.marked_for_destruction? or li.new_record? or li.quantity_changed? or li.catalog_item_id_changed? } end |
#goods_changed? ⇒ Boolean
148 149 150 |
# File 'app/models/line_item_extension.rb', line 148 def goods_changed? lines_changed.any?(&:is_goods?) end |
#grouped_by_category ⇒ Object
67 68 69 |
# File 'app/models/line_item_extension.rb', line 67 def grouped_by_category product_category_sorted.group_by(&:category_name) end |
#has_goods_lines? ⇒ Boolean
144 145 146 |
# File 'app/models/line_item_extension.rb', line 144 def has_goods_lines? active_goods_lines.present? end |
#has_goods_without_shipping? ⇒ Boolean
152 153 154 |
# File 'app/models/line_item_extension.rb', line 152 def has_goods_without_shipping? (has_goods_lines? and !has_shipping_line?) end |
#has_shipping_line? ⇒ Boolean
140 141 142 |
# File 'app/models/line_item_extension.rb', line 140 def has_shipping_line? active_shipping_lines.present? end |
#has_underlayment? ⇒ Boolean
63 64 65 |
# File 'app/models/line_item_extension.rb', line 63 def has_underlayment? to_a.map(&:item_id).intersect?(Item.underlayments.ids) end |
#lines_changed ⇒ Object
112 113 114 |
# File 'app/models/line_item_extension.rb', line 112 def lines_changed to_a.select { |li| li.changed? or li.destroyed? or li.marked_for_destruction? } end |
#lines_changed? ⇒ Boolean
116 117 118 |
# File 'app/models/line_item_extension.rb', line 116 def lines_changed? to_a.any? { |li| li.changed? or li.destroyed? or li.marked_for_destruction? or li.new_record? } end |
#primary_heating_element_spacing ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'app/models/line_item_extension.rb', line 45 def primary_heating_element_spacing # just count the occurents of line items for a given heating_element_spacing he_spacings = heating_elements.filter_map { |li| li.item&.spec(:heating_element_spacing)&.dig(:raw) } if he_spacings.empty? nil else freq = he_spacings.each_with_object(Hash.new(0)) do |v, h| h[v] += 1 end he_spacings.max_by { |v| freq[v] } end end |
#primary_heating_system_product_line ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'app/models/line_item_extension.rb', line 18 def primary_heating_system_product_line # just count the occurrences of line items for a given heating system type hs_product_lines = heating_elements.parents_only.select { |li| li.item.primary_product_line&.get_first_heating_system_type.present? }.filter_map { |li| li.item.primary_product_line.get_first_heating_system_type } hs_product_lines = heating_elements.select { |li| li.item.primary_product_line&.get_first_heating_system_type.present? }.filter_map { |li| li.item.primary_product_line.get_first_heating_system_type } if hs_product_lines.empty? if hs_product_lines.empty? nil else freq = hs_product_lines.each_with_object(Hash.new(0)) do |v, h| h[v] += 1 end hs_product_lines.max_by { |v| freq[v] } end end |
#primary_heating_system_voltage ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'app/models/line_item_extension.rb', line 32 def primary_heating_system_voltage # just count the occurents of line items for a given heating system voltage hs_volts = heating_elements.filter_map { |li| li.item&.voltage } if hs_volts.empty? nil else freq = hs_volts.each_with_object(Hash.new(0)) do |v, h| h[v] += 1 end hs_volts.max_by { |v| freq[v] } end end |
#signature ⇒ Object
156 157 158 |
# File 'app/models/line_item_extension.rb', line 156 def signature Delivery.md5_hash_items(active_lines) end |
#ungrouped ⇒ Object
71 72 73 |
# File 'app/models/line_item_extension.rb', line 71 def ungrouped where(room_configuration_id: nil) end |
#version ⇒ Object
14 15 16 |
# File 'app/models/line_item_extension.rb', line 14 def version all.maximum(:updated_at).to_i end |
#with_discount_preloads ⇒ Object
Preload associations commonly needed for coupon qualification and discount calculations.
This prevents N+1 queries when:
- item: checking item specifications (sqft, linear_ft), product line, etc.
- catalog_item: calculating msrp_total
- line_discounts: checking if discount applies to line_item, calculating discounted_total
Returns a relation - call .active_parent_lines after this to get the filtered array.
94 95 96 |
# File 'app/models/line_item_extension.rb', line 94 def with_discount_preloads includes(:item, :catalog_item, :line_discounts) end |