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
107 108 109 |
# File 'app/models/line_item_extension.rb', line 107 def active_goods_lines active_lines.select(&:is_goods?) end |
#active_lines ⇒ Object
58 59 60 61 |
# File 'app/models/line_item_extension.rb', line 58 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
81 82 83 84 85 86 87 88 89 |
# File 'app/models/line_item_extension.rb', line 81 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
77 78 79 |
# File 'app/models/line_item_extension.rb', line 77 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
99 100 101 |
# File 'app/models/line_item_extension.rb', line 99 def active_non_shipping_lines active_lines.reject(&:is_shipping?) end |
#active_parent_lines ⇒ Object
63 64 65 |
# File 'app/models/line_item_extension.rb', line 63 def active_parent_lines active_lines.reject { |li| li.parent_id.present? } end |
#active_services_lines ⇒ Object
111 112 113 |
# File 'app/models/line_item_extension.rb', line 111 def active_services_lines active_lines.select(&:is_service?) end |
#active_shipping_lines ⇒ Object
103 104 105 |
# File 'app/models/line_item_extension.rb', line 103 def active_shipping_lines active_lines.select(&:is_shipping?) end |
#active_tax_unclassed_lines ⇒ Object
115 116 117 |
# File 'app/models/line_item_extension.rb', line 115 def active_tax_unclassed_lines active_lines.select(&:is_tax_unclassed?) end |
#any_require_cable_spacing? ⇒ Boolean
37 38 39 40 |
# File 'app/models/line_item_extension.rb', line 37 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
54 55 56 |
# File 'app/models/line_item_extension.rb', line 54 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
127 128 129 |
# File 'app/models/line_item_extension.rb', line 127 def goods_changed? lines_changed.any?(&:is_goods?) end |
#grouped_by_category ⇒ Object
46 47 48 |
# File 'app/models/line_item_extension.rb', line 46 def grouped_by_category product_category_sorted.group_by(&:category_name) end |
#has_goods_lines? ⇒ Boolean
123 124 125 |
# File 'app/models/line_item_extension.rb', line 123 def has_goods_lines? active_goods_lines.present? end |
#has_goods_without_shipping? ⇒ Boolean
131 132 133 |
# File 'app/models/line_item_extension.rb', line 131 def has_goods_without_shipping? (has_goods_lines? and !has_shipping_line?) end |
#has_shipping_line? ⇒ Boolean
119 120 121 |
# File 'app/models/line_item_extension.rb', line 119 def has_shipping_line? active_shipping_lines.present? end |
#has_underlayment? ⇒ Boolean
42 43 44 |
# File 'app/models/line_item_extension.rb', line 42 def has_underlayment? to_a.map(&:item_id).intersect?(Item.underlayments.ids) end |
#lines_changed ⇒ Object
91 92 93 |
# File 'app/models/line_item_extension.rb', line 91 def lines_changed to_a.select { |li| li.changed? or li.destroyed? or li.marked_for_destruction? } end |
#lines_changed? ⇒ Boolean
95 96 97 |
# File 'app/models/line_item_extension.rb', line 95 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
31 32 33 34 35 |
# File 'app/models/line_item_extension.rb', line 31 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) } he_spacings.tally.max_by(&:last)&.first end |
#primary_heating_system_product_line ⇒ Object
18 19 20 21 22 23 |
# 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? hs_product_lines.tally.max_by(&:last)&.first end |
#primary_heating_system_voltage ⇒ Object
25 26 27 28 29 |
# File 'app/models/line_item_extension.rb', line 25 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 } hs_volts.tally.max_by(&:last)&.first end |
#signature ⇒ Object
135 136 137 |
# File 'app/models/line_item_extension.rb', line 135 def signature Delivery.md5_hash_items(active_lines) end |
#ungrouped ⇒ Object
50 51 52 |
# File 'app/models/line_item_extension.rb', line 50 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.
73 74 75 |
# File 'app/models/line_item_extension.rb', line 73 def with_discount_preloads includes(:item, :catalog_item, :line_discounts) end |