Class: LineItem

Overview

== Schema Information

Table name: line_items
Database name: primary

id :integer not null, primary key
children_count :integer
cm_category :string(255)
description :text
destination_state_code :string(255)
discounted_price :decimal(10, 2) default(0.0), not null
edi_line_number :integer
edi_reference :string
edi_unit_cost :decimal(10, 2)
ignore_tax :boolean
origin_state_code :string(255)
price :decimal(10, 2) not null
qty_shipped :integer
quantity :integer not null
redemption_code :string(255)
resource_type :string(255)
reviewed :boolean default(FALSE), not null
sequence :integer
tax_class :string(255)
tax_only :boolean
tax_rate :decimal(10, 6)
tax_total :decimal(10, 2)
tax_type :string
taxable_amount :decimal(12, 2)
total_cogs :decimal(10, 4)
unit_cogs :decimal(10, 4)
created_at :datetime
updated_at :datetime
backup_catalog_item_id :integer
business_unit_id :integer
catalog_item_id :integer
creator_id :integer
credit_order_line_item_id :integer
credit_rma_item_id :integer
delivery_id :integer
item_id :integer
ledger_company_account_id :integer
legacy_iq_room_line_item_option_group_id :integer
parent_id :integer
replacement_rma_item_id :integer
resource_id :integer
resource_tax_rate_id :integer
room_configuration_id :integer
serial_number_id :integer
shipping_cost_id :integer
store_item_id :integer
updater_id :integer

Indexes

idx_catalog_item_id_delivery_id (catalog_item_id,delivery_id)
idx_delivery_id_item_id_tax_class (delivery_id,item_id,tax_class)
idx_resource_type_resource_id_ci_id (resource_type,resource_id,catalog_item_id)
index_line_items_on_credit_rma_item_id (credit_rma_item_id) WHERE (credit_rma_item_id IS NOT NULL) USING hash
index_line_items_on_delivery_id (delivery_id) WHERE (delivery_id IS NOT NULL) USING hash
index_line_items_on_parent_id (parent_id) USING hash
index_line_items_on_replacement_rma_item_id (replacement_rma_item_id) WHERE (replacement_rma_item_id IS NOT NULL) USING hash
index_line_items_on_resource_type_and_item_id (resource_type,item_id)
index_line_items_on_room_configuration_id (room_configuration_id) WHERE (room_configuration_id IS NOT NULL) USING hash
index_line_items_on_shipping_cost_id (shipping_cost_id) WHERE (shipping_cost_id IS NOT NULL) USING hash
line_items_delivery_id_dup_idx (resource_type,resource_id,item_id,delivery_id) UNIQUE WHERE ((tax_class)::text = 'shp'::text)
line_items_resource_id_index (resource_id)

Foreign Keys

fk_rails_... (catalog_item_id => catalog_items.id)
line_items_catalog_item_id_fk (catalog_item_id => catalog_items.id)
line_items_delivery_id_fk (delivery_id => deliveries.id) ON DELETE => nullify
line_items_room_configuration_id_fk (room_configuration_id => room_configurations.id) ON DELETE => nullify
line_items_shipping_costs_id_fk (shipping_cost_id => shipping_costs.id) ON DELETE => cascade

Constant Summary collapse

LINE_ITEM_TAX_CLASSES =

Line item tax classes.

{ g: 'GOODS', svc: 'SERVICE', shp: 'FREIGHT', none: 'NONE' }.freeze
LINE_ITEM_TAX_CLASSES_SORT_ORDER =

Line item tax classes sort order.

%i[g svc shp none].freeze

Constants included from Models::InventoryCommittable

Models::InventoryCommittable::STATES_WITH_NO_EXPIRATION

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Attributes included from Models::TaxableLine

#do_not_calculate_tax

Belongs to collapse

Methods included from Models::TaxableLine

#destination_state, #origin_state, #resource_tax_rate

Methods included from Models::Auditable

#creator, #updater

Has one collapse

Has many collapse

Methods included from Models::Reservable

#reserved_serial_numbers

Delegated Instance Attributes 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::TaxableLine

#calculate_tax, #has_tax?, #is_international?, #set_initial_tax_rate, #update_tax_rate

Methods included from Models::InventoryCommittable

#can_be_committed?, #can_be_uncommitted?, #commit_line_items, #determine_commit_expiration_date, #has_committed_line_items?, #uncommit_line_items

Methods included from Models::Reservable

#all_reserved_serial_numbers, #all_reserved_serial_numbers_available?, #auto_reserve_serial_numbers, #available_serial_numbers, #commit_reserved_serial_numbers, #fully_reserved?, #kit_components_requires_reservation?, #link_serial_numbers, #rejoin_serial_numbers, #require_reservation?, #reservable_item, #reservable_store_item, #serial_numbers, #show_serial_number_toggle?, #skip_reservation_screen?, #split_serial_numbers, #total_reserved_serial_numbers, #uncommit_reserved_serial_numbers, #unlink_serial_numbers, #update_serial_numbers_shipped_count

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 Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#business_unit_idObject (readonly)



131
# File 'app/models/line_item.rb', line 131

validates :ledger_company_account_id, :business_unit_id, presence: { if: proc { |li| (li.cm_category == 'Misc') && li.linked_to_standalone_credit_memo } }

#cm_categoryObject (readonly)



130
# File 'app/models/line_item.rb', line 130

validates :cm_category, presence: { if: proc { |li| li.linked_to_credit_memo || li.linked_to_invoice } }

#discounted_priceObject (readonly)



138
# File 'app/models/line_item.rb', line 138

validates :discounted_price, numericality: { equal_to: 0, if: :tax_only?, message: 'must be equal to 0 for tax only lines' }

#force_catalog_validationObject

Returns the value of attribute force_catalog_validation.



222
223
224
# File 'app/models/line_item.rb', line 222

def force_catalog_validation
  @force_catalog_validation
end

#item_idObject (readonly)



133
# File 'app/models/line_item.rb', line 133

validates :item_id, presence: { if: proc { |li| (li.cm_category == 'Item') && li.linked_to_standalone_invoice } }

#item_nameObject

Returns the value of attribute item_name.



222
223
224
# File 'app/models/line_item.rb', line 222

def item_name
  @item_name
end

#ledger_company_account_idObject (readonly)



131
# File 'app/models/line_item.rb', line 131

validates :ledger_company_account_id, :business_unit_id, presence: { if: proc { |li| (li.cm_category == 'Misc') && li.linked_to_standalone_credit_memo } }

#original_delivery_idObject

Returns the value of attribute original_delivery_id.



222
223
224
# File 'app/models/line_item.rb', line 222

def original_delivery_id
  @original_delivery_id
end

#qty_availableObject

Returns the value of attribute qty_available.



222
223
224
# File 'app/models/line_item.rb', line 222

def qty_available
  @qty_available
end

#quantityObject (readonly)



136
# File 'app/models/line_item.rb', line 136

validates :quantity, presence: true

#sku_overrideObject

Returns the value of attribute sku_override.



222
223
224
# File 'app/models/line_item.rb', line 222

def sku_override
  @sku_override
end

#tax_classObject (readonly)



132
# File 'app/models/line_item.rb', line 132

validates :tax_class, :taxable_amount, presence: { if: :linked_to_standalone_credit_memo_or_invoice }

#taxable_amountObject (readonly)



132
# File 'app/models/line_item.rb', line 132

validates :tax_class, :taxable_amount, presence: { if: :linked_to_standalone_credit_memo_or_invoice }

#unit_cogsObject (readonly)



134
# File 'app/models/line_item.rb', line 134

validates :unit_cogs, presence: { if: proc { |li| (li.cm_category == 'Item') && li.linked_to_invoice } }

Class Method Details

.accessoriesActiveRecord::Relation<LineItem>

A relation of LineItems that are accessories. Active Record Scope

Returns:

See Also:



156
# File 'app/models/line_item.rb', line 156

scope :accessories, -> { joins(:item).merge(Item.accessories) }

.availability_hash(line_items) ⇒ Object



554
555
556
557
558
559
560
561
562
563
564
565
# File 'app/models/line_item.rb', line 554

def self.availability_hash(line_items)
  result = {}
  line_items.each do |li|
    next unless li.catalog_item

    cat_item_id = li.catalog_item.id
    result[cat_item_id] ||= { 'available' => li.catalog_item.qty_available, 'ordered' => 0, 'in_stock' => li.catalog_item.qty_available.positive? }
    result[cat_item_id]['ordered'] += li.quantity
    result[cat_item_id]['in_stock'] = false if result[cat_item_id]['ordered'] > result[cat_item_id]['available']
  end
  result
end

.bstockActiveRecord::Relation<LineItem>

A relation of LineItems that are bstock. Active Record Scope

Returns:

See Also:



210
# File 'app/models/line_item.rb', line 210

scope :bstock, -> { joins(:item).where(items: { condition: 'refurbished' }) }

.by_product_category_pathActiveRecord::Relation<LineItem>

A relation of LineItems that are by product category path. Active Record Scope

Returns:

See Also:



193
# File 'app/models/line_item.rb', line 193

scope :by_product_category_path, ->(slug_path) { joins(:item).merge(Item.by_product_category_path(slug_path)) }

.by_product_category_urlActiveRecord::Relation<LineItem>

A relation of LineItems that are by product category url. Active Record Scope

Returns:

See Also:



189
# File 'app/models/line_item.rb', line 189

scope :by_product_category_url, ->(url) { joins(:item).merge(Item.by_product_category_url(url)) }

.by_product_line_pathActiveRecord::Relation<LineItem>

A relation of LineItems that are by product line path. Active Record Scope

Returns:

See Also:



192
# File 'app/models/line_item.rb', line 192

scope :by_product_line_path, ->(slug_path) { joins(:item).merge(Item.by_product_line_path(slug_path)) }

.by_product_line_urlActiveRecord::Relation<LineItem>

A relation of LineItems that are by product line url. Active Record Scope

Returns:

See Also:



190
# File 'app/models/line_item.rb', line 190

scope :by_product_line_url, ->(url) { joins(:item).merge(Item.by_product_line_url(url)) }

.cold_leadsActiveRecord::Relation<LineItem>

A relation of LineItems that are cold leads. Active Record Scope

Returns:

See Also:



175
# File 'app/models/line_item.rb', line 175

scope :cold_leads, -> { joins(:item).merge(Item.cold_leads) }

.collect_product_lines(line_items) ⇒ Object



260
261
262
# File 'app/models/line_item.rb', line 260

def self.collect_product_lines(line_items)
  line_items.filter_map { |li| li&.item&.product_lines }.flatten.uniq
end

.compare(source_line_items, target_line_items) ⇒ Object



400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'app/models/line_item.rb', line 400

def self.compare(source_line_items, target_line_items)
  report = {}
  all_matched_line_ids = []
  source_line_items.each do |source_line|
    matched_lines_in_target = target_line_items.select { |target_line| target_line.catalog_item_id == source_line.catalog_item_id }
    if matched_lines_in_target.empty?
      report[source_line] = { status: :not_found, note: 'No matching lines found in target', matching_line_ids: [] }
    else
      matched_line_ids = matched_lines_in_target.map(&:id)
      all_matched_line_ids += matched_line_ids
      report[source_line] = { status: :ok, note: "Found similar line at line id #{matched_line_ids.join(',')}", matching_line_ids: matched_line_ids }
    end
  end
  # Anything left?
  leftover_line_ids = target_line_items.reject { |tl| all_matched_line_ids.include? tl.id }.map(&:id)
  report['extra_lines'] = { status: :extra, note: "Found extra lines id #{leftover_line_ids.join(',')}", matching_line_ids: leftover_line_ids } unless leftover_line_ids.empty?
  report
end

.controlsActiveRecord::Relation<LineItem>

A relation of LineItems that are controls. Active Record Scope

Returns:

See Also:



157
# File 'app/models/line_item.rb', line 157

scope :controls, -> { joins(:item).merge(Item.controls) }

.countertop_heatersActiveRecord::Relation<LineItem>

A relation of LineItems that are countertop heaters. Active Record Scope

Returns:

See Also:



188
# File 'app/models/line_item.rb', line 188

scope :countertop_heaters, -> { joins(:item).merge(Item.countertop_heaters) }

.custom_matsActiveRecord::Relation<LineItem>

A relation of LineItems that are custom mats. Active Record Scope

Returns:

See Also:



187
# File 'app/models/line_item.rb', line 187

scope :custom_mats, -> { joins(:item).merge(Item.custom_mats) }

.dropshipActiveRecord::Relation<LineItem>

A relation of LineItems that are dropship. Active Record Scope

Returns:

See Also:



169
# File 'app/models/line_item.rb', line 169

scope :dropship, -> { joins(:item).merge(Item.dropships) }

.easystatsActiveRecord::Relation<LineItem>

A relation of LineItems that are easystats. Active Record Scope

Returns:

See Also:



165
# File 'app/models/line_item.rb', line 165

scope :easystats, -> { joins(:item).merge(Item.easystats) }

.electrical_plan_controlsActiveRecord::Relation<LineItem>

A relation of LineItems that are electrical plan controls. Active Record Scope

Returns:

See Also:



159
# File 'app/models/line_item.rb', line 159

scope :electrical_plan_controls, -> { joins(:item).merge(Item.electrical_plan_controls) }

.floor_heating_elementsActiveRecord::Relation<LineItem>

A relation of LineItems that are floor heating elements. Active Record Scope

Returns:

See Also:



154
# File 'app/models/line_item.rb', line 154

scope :floor_heating_elements, -> { joins(:item).merge(Item.floor_heating_elements) }

.goodsActiveRecord::Relation<LineItem>

A relation of LineItems that are goods. Active Record Scope

Returns:

See Also:



178
# File 'app/models/line_item.rb', line 178

scope :goods,    -> { where(tax_class: 'g') }

.group_by_category(line_items) ⇒ Object



331
332
333
334
335
336
337
338
339
340
# File 'app/models/line_item.rb', line 331

def self.group_by_category(line_items)
  line_items = line_items.to_a.sort_by(&:sort_priority)
  all_items = line_items.group_by { |li| li.item&.product_category_name || 'Not Categorized' }

  # Use cached priorities from ProductCategoryConstants
  all_items.sort_by do |_name, items|
    category_id = items.first&.item&.product_category_id
    ProductCategoryConstants.priority_for(category_id)
  end
end

.has_floor_sensorActiveRecord::Relation<LineItem>

A relation of LineItems that are has floor sensor. Active Record Scope

Returns:

See Also:



181
# File 'app/models/line_item.rb', line 181

scope :has_floor_sensor, -> { joins(:item).merge(Item.has_floor_sensor) }

.heating_elementsActiveRecord::Relation<LineItem>

A relation of LineItems that are heating elements. Active Record Scope

Returns:

See Also:



151
# File 'app/models/line_item.rb', line 151

scope :heating_elements, -> { joins(:item).merge(Item.heating_elements) }

.install_kitsActiveRecord::Relation<LineItem>

A relation of LineItems that are install kits. Active Record Scope

Returns:

See Also:



184
# File 'app/models/line_item.rb', line 184

scope :install_kits, -> { joins(:item).merge(Item.install_kits) }

.insulationsActiveRecord::Relation<LineItem>

A relation of LineItems that are insulations. Active Record Scope

Returns:

See Also:



185
# File 'app/models/line_item.rb', line 185

scope :insulations, -> { joins(:item).merge(Item.insulations) }

.integration_kitsActiveRecord::Relation<LineItem>

A relation of LineItems that are integration kits. Active Record Scope

Returns:

See Also:



167
# File 'app/models/line_item.rb', line 167

scope :integration_kits, -> { joins(:item).merge(Item.integration_kits) }

.inventory_check(container) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'app/models/line_item.rb', line 268

def self.inventory_check(container)
  lines = container.line_items.non_shipping
                   .includes(:item, :direct_store_item, :inventory_commits, { catalog_item: :store_item })

  # Per-line check: handles dropship, unlimited inventory, fully-committed lines, negative qty.
  return :none unless lines.all?(&:is_fulfillable?)

  # Aggregate check: multiple non-shipping lines targeting the same SKU — whether duplicate
  # standalone lines or kit component children overlapping with standalone lines — must not
  # together exceed the shared qty_available pool. The per-line check misses this because
  # each line independently sees the full pool without deducting the other lines' demand.
  #
  # Cache from_store_id once: all lines share the same resource (container). This mirrors
  # the stock_status StoreItem routing (line_item.rb#stock_status) without an association
  # load per line.
  from_store_id = container.respond_to?(:from_store_id) ? container.from_store_id : nil

  # Key demand and store_items by store_item ID so that all lines drawing from the same
  # physical stock pool are always aggregated together, regardless of which catalog_item
  # they arrived through. This also handles from_store_id and cross-catalog edge cases.
  uncommitted_demand = Hash.new(0)
  store_item_by_id   = {}

  lines.each do |li|
    next unless li.catalog_item_id
    next if li.quantity.negative?
    # Route through the per-line helper so that RoomConfiguration resources (which always
    # return false from single_origin_or_warehouse_pickup?) are handled identically to the
    # per-line check. In practice, container.line_items only returns Order-resource lines,
    # but this keeps the logic consistent with stock_status.
    next if li.dropship? && !li.single_origin_or_warehouse_pickup?
    next if li.unlimited_inventory?

    committed_quantity = li.inventory_commits.sum(&:quantity)
    # Already fully reserved — committed units have already reduced qty_available globally.
    next if committed_quantity >= li.quantity

    # Add only the truly uncommitted portion. Committed units are already subtracted from
    # qty_available globally, so adding the full quantity would double-count the committed
    # share against the same pool it was already deducted from.
    uncommitted_qty = li.quantity - committed_quantity

    # Mirror stock_status's StoreItem routing so the aggregate compares against the correct
    # store's pool. For store-transfer orders (from_store_id set), this fires one SQL query
    # per line — consistent with the existing per-line behavior in stock_status.
    si = if from_store_id
           StoreItem.where(store_id: from_store_id, item_id: li.item_id, location: 'AVAILABLE').first
         else
           li.store_item
         end
    next unless si

    uncommitted_demand[si.id] += uncommitted_qty
    store_item_by_id[si.id] ||= si
  end

  store_item_by_id.each do |si_id, si|
    return :none if si.qty_available < uncommitted_demand[si_id]
  end

  :ok
end

.kitsActiveRecord::Relation<LineItem>

A relation of LineItems that are kits. Active Record Scope

Returns:

See Also:



204
# File 'app/models/line_item.rb', line 204

scope :kits, -> { joins(:item).merge(Item.kits) }

.membranesActiveRecord::Relation<LineItem>

A relation of LineItems that are membranes. Active Record Scope

Returns:

See Also:



186
# File 'app/models/line_item.rb', line 186

scope :membranes, -> { joins(:item).merge(Item.membranes) }

.must_be_shipped_insuredActiveRecord::Relation<LineItem>

A relation of LineItems that are must be shipped insured. Active Record Scope

Returns:

See Also:



194
# File 'app/models/line_item.rb', line 194

scope :must_be_shipped_insured, -> { with_associations.where('store_items.must_be_shipped_insured IS TRUE') }

.no_tax_classActiveRecord::Relation<LineItem>

A relation of LineItems that are no tax class. Active Record Scope

Returns:

See Also:



180
# File 'app/models/line_item.rb', line 180

scope :no_tax_class, -> { where(tax_class: 'none') }

.non_dropshipActiveRecord::Relation<LineItem>

A relation of LineItems that are non dropship. Active Record Scope

Returns:

See Also:



170
# File 'app/models/line_item.rb', line 170

scope :non_dropship, -> { joins(:item).merge(Item.non_dropships) }

.non_shippingActiveRecord::Relation<LineItem>

A relation of LineItems that are non shipping. Active Record Scope

Returns:

See Also:



176
# File 'app/models/line_item.rb', line 176

scope :non_shipping, -> { where(tax_class: %w[g svc none]) }

.oj_programmable_tstatsActiveRecord::Relation<LineItem>

A relation of LineItems that are oj programmable tstats. Active Record Scope

Returns:

See Also:



163
# File 'app/models/line_item.rb', line 163

scope :oj_programmable_tstats, -> { joins(:item).merge(Item.oj_programmable_tstats) }

.oj_tstatsActiveRecord::Relation<LineItem>

A relation of LineItems that are oj tstats. Active Record Scope

Returns:

See Also:



162
# File 'app/models/line_item.rb', line 162

scope :oj_tstats, -> { joins(:item).merge(Item.oj_tstats) }

.order_by_specActiveRecord::Relation<LineItem>

A relation of LineItems that are order by spec. Active Record Scope

Returns:

See Also:



153
# File 'app/models/line_item.rb', line 153

scope :order_by_spec, ->(spec, datatype = 'varchar', direction = 'asc') { joins(:item).merge(Item.order_by_spec(spec, datatype, direction)) }

.power_modulesActiveRecord::Relation<LineItem>

A relation of LineItems that are power modules. Active Record Scope

Returns:

See Also:



166
# File 'app/models/line_item.rb', line 166

scope :power_modules, -> { joins(:item).merge(Item.power_modules) }

.powersActiveRecord::Relation<LineItem>

A relation of LineItems that are powers. Active Record Scope

Returns:

See Also:



158
# File 'app/models/line_item.rb', line 158

scope :powers, -> { joins(:item).merge(Item.powers) }

.product_category_sortedActiveRecord::Relation<LineItem>

A relation of LineItems that are product category sorted. Active Record Scope

Returns:

See Also:



149
# File 'app/models/line_item.rb', line 149

scope :product_category_sorted, -> { includes(item: :product_category).joins(item: :product_category).order(ProductCategory[:priority]) }

.relay_panelsActiveRecord::Relation<LineItem>

A relation of LineItems that are relay panels. Active Record Scope

Returns:

See Also:



172
# File 'app/models/line_item.rb', line 172

scope :relay_panels, -> { joins(:item).merge(Item.relay_panels) }

.reservableActiveRecord::Relation<LineItem>

A relation of LineItems that are reservable. Active Record Scope

Returns:

See Also:



182
# File 'app/models/line_item.rb', line 182

scope :reservable, -> { joins(:item).merge(Item.reservable) }

.rough_in_kitsActiveRecord::Relation<LineItem>

A relation of LineItems that are rough in kits. Active Record Scope

Returns:

See Also:



183
# File 'app/models/line_item.rb', line 183

scope :rough_in_kits, -> { joins(:item).merge(Item.rough_in_kits) }

.sensorsActiveRecord::Relation<LineItem>

A relation of LineItems that are sensors. Active Record Scope

Returns:

See Also:



168
# File 'app/models/line_item.rb', line 168

scope :sensors, -> { joins(:item).merge(Item.sensors) }

.servicesActiveRecord::Relation<LineItem>

A relation of LineItems that are services. Active Record Scope

Returns:

See Also:



179
# File 'app/models/line_item.rb', line 179

scope :services, -> { where(tax_class: 'svc') }

.shipping_onlyActiveRecord::Relation<LineItem>

A relation of LineItems that are shipping only. Active Record Scope

Returns:

See Also:



177
# File 'app/models/line_item.rb', line 177

scope :shipping_only, -> { where(tax_class: 'shp') }

.sku_and_aliases_searchActiveRecord::Relation<LineItem>

A relation of LineItems that are sku and aliases search. Active Record Scope

Returns:

See Also:



211
# File 'app/models/line_item.rb', line 211

scope :sku_and_aliases_search, ->(sku) { joins(:item).merge(Item.sku_and_aliases_search(sku)) }

.smartfit_itemsActiveRecord::Relation<LineItem>

A relation of LineItems that are smartfit items. Active Record Scope

Returns:

See Also:



199
# File 'app/models/line_item.rb', line 199

scope :smartfit_items, -> { by_product_line_path(LtreePaths::PL_SERVICES_SMARTFIT) }

.smartfix_itemsActiveRecord::Relation<LineItem>

A relation of LineItems that are smartfix items. Active Record Scope

Returns:

See Also:



201
# File 'app/models/line_item.rb', line 201

scope :smartfix_items, -> { by_product_line_path(LtreePaths::PL_SERVICES_SMARTFIX) }

.smartguide_itemsActiveRecord::Relation<LineItem>

A relation of LineItems that are smartguide items. Active Record Scope

Returns:

See Also:



202
# File 'app/models/line_item.rb', line 202

scope :smartguide_items, -> { by_product_line_path(LtreePaths::PL_SERVICES_SMARTGUIDE) }

.smartinstall_itemsActiveRecord::Relation<LineItem>

A relation of LineItems that are smartinstall items. Active Record Scope

Returns:

See Also:



200
# File 'app/models/line_item.rb', line 200

scope :smartinstall_items, -> { by_product_line_path(LtreePaths::PL_SERVICES_SMARTINSTALL) }

.smartpresetsActiveRecord::Relation<LineItem>

A relation of LineItems that are smartpresets. Active Record Scope

Returns:

See Also:



174
# File 'app/models/line_item.rb', line 174

scope :smartpresets, -> { joins(:item).merge(Item.smartpresets) }

.smartservice_itemsActiveRecord::Relation<LineItem>

A relation of LineItems that are smartservice items. Active Record Scope

Returns:

See Also:



203
# File 'app/models/line_item.rb', line 203

scope :smartservice_items, -> { joins(:item).merge(Item.smart_services) }

.smartstatsActiveRecord::Relation<LineItem>

A relation of LineItems that are smartstats. Active Record Scope

Returns:

See Also:



164
# File 'app/models/line_item.rb', line 164

scope :smartstats, -> { joins(:item).merge(Item.smartstats) }

.snow_melting_elementsActiveRecord::Relation<LineItem>

A relation of LineItems that are snow melting elements. Active Record Scope

Returns:

See Also:



155
# File 'app/models/line_item.rb', line 155

scope :snow_melting_elements, -> { joins(:item).merge(Item.snow_melting_elements) }

.sort_for_planActiveRecord::Relation<LineItem>

A relation of LineItems that are sort for plan. Active Record Scope

Returns:

See Also:



160
# File 'app/models/line_item.rb', line 160

scope :sort_for_plan, -> { order_by_spec('amps', 'decimal', 'asc') }

.tempzone_onlyActiveRecord::Relation<LineItem>

A relation of LineItems that are tempzone only. Active Record Scope

Returns:

See Also:



161
# File 'app/models/line_item.rb', line 161

scope :tempzone_only, -> { joins(:item).merge(Item.tempzones) }

.tempzone_or_environ_heating_elementsActiveRecord::Relation<LineItem>

A relation of LineItems that are tempzone or environ heating elements. Active Record Scope

Returns:

See Also:



195
196
197
198
# File 'app/models/line_item.rb', line 195

scope :tempzone_or_environ_heating_elements, -> {
  heating_elements.joins(:item).where(Item[:primary_pl_path_slugs].ltree_descendant(LtreePaths::PL_FLOOR_HEATING_TEMPZONE))
                  .or(heating_elements.joins(:item).where(Item[:primary_pl_path_slugs].ltree_descendant(LtreePaths::PL_FLOOR_HEATING_ENVIRON)))
}

.thermostatsActiveRecord::Relation<LineItem>

A relation of LineItems that are thermostats. Active Record Scope

Returns:

See Also:



171
# File 'app/models/line_item.rb', line 171

scope :thermostats, -> { joins(:item).merge(Item.thermostats) }

.total_of_collection(line_items) ⇒ Object



264
265
266
# File 'app/models/line_item.rb', line 264

def self.total_of_collection(line_items)
  line_items.to_a.sum(&:total).round(2)
end

.underlaymentsActiveRecord::Relation<LineItem>

A relation of LineItems that are underlayments. Active Record Scope

Returns:

See Also:



173
# File 'app/models/line_item.rb', line 173

scope :underlayments, -> { joins(:item).merge(Item.underlayments) }

.with_associationsActiveRecord::Relation<LineItem>

A relation of LineItems that are with associations. Active Record Scope

Returns:

See Also:



150
# File 'app/models/line_item.rb', line 150

scope :with_associations, -> { includes(:item, catalog_item: { store_item: :item }).references(:item, catalog_item: { store_item: :item }) }

.with_positive_qtyActiveRecord::Relation<LineItem>

A relation of LineItems that are with positive qty. Active Record Scope

Returns:

See Also:



209
# File 'app/models/line_item.rb', line 209

scope :with_positive_qty, -> { where('quantity > 0') }

.with_product_specificationActiveRecord::Relation<LineItem>

A relation of LineItems that are with product specification. Active Record Scope

Returns:

See Also:



152
# File 'app/models/line_item.rb', line 152

scope :with_product_specification, ->(token, value, grouping = nil) { joins(:item).merge(Item.with_product_specification(token, value, grouping)) }

.with_reserved_serial_numbers_countActiveRecord::Relation<LineItem>

A relation of LineItems that are with reserved serial numbers count. Active Record Scope

Returns:

See Also:



208
# File 'app/models/line_item.rb', line 208

scope :with_reserved_serial_numbers_count, -> { all.select_append('(select count(rsn.id) from reserved_serial_numbers rsn where rsn.line_item_id = line_items.id) as reserved_serial_numbers_count') }

.with_serial_numbers_countActiveRecord::Relation<LineItem>

A relation of LineItems that are with serial numbers count. Active Record Scope

Returns:

See Also:



207
# File 'app/models/line_item.rb', line 207

scope :with_serial_numbers_count, -> { all.select_append('(select count(sn.id) from serial_numbers sn where sn.line_item_id = line_items.id) as serial_numbers_count') }

.without_childrenActiveRecord::Relation<LineItem>

A relation of LineItems that are without children. Active Record Scope

Returns:

See Also:



205
# File 'app/models/line_item.rb', line 205

scope :without_children, -> { where(children_count: [nil, 0]) }

Instance Method Details

#all_quantities_allocated_to_shipments?Boolean

Returns:

  • (Boolean)


429
430
431
# File 'app/models/line_item.rb', line 429

def all_quantities_allocated_to_shipments?
  remaining_quantity_to_allocate_to_shipments == 0
end

#as_jsonObject



859
860
861
# File 'app/models/line_item.rb', line 859

def as_json
  super(methods: %i[name sku get_item_id])
end

#business_unitBusinessUnit



115
# File 'app/models/line_item.rb', line 115

belongs_to :business_unit, optional: true

#calculated_tax_classObject



469
470
471
472
473
# File 'app/models/line_item.rb', line 469

def calculated_tax_class
  return tax_class if tax_class

  item.nil? ? tax_class : item.tax_class
end

#catalog_itemCatalogItem



98
# File 'app/models/line_item.rb', line 98

belongs_to :catalog_item, inverse_of: :line_items, optional: true

#category_nameObject

Alias for Item#category_name

Returns:

  • (Object)

    Item#category_name

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#children_description(short = false, with_brackets = false) ⇒ Object



628
629
630
631
632
633
634
635
636
637
638
# File 'app/models/line_item.rb', line 628

def children_description(short = false, with_brackets = false)
  contents = nil
  if children.any?
    contents = ''
    contents += "The #{sku} is " unless short
    contents += 'composed of '
    contents += children.map { |child| "#{child.quantity} x #{child.item.sku}" }.to_sentence(last_word_connector: ' and ')
    contents = "(#{contents})" if with_brackets
  end
  contents
end

#cogs_markupObject



746
747
748
# File 'app/models/line_item.rb', line 746

def cogs_markup
  1.1 # 10% markup for packaging and unapplied landed cost
end

#company_account_refObject



465
466
467
# File 'app/models/line_item.rb', line 465

def 
  &.identifier
end

#copy_discounted_priceObject (protected)



928
929
930
# File 'app/models/line_item.rb', line 928

def copy_discounted_price
  self.price = discounted_price
end

#coupon_amountObject



425
426
427
# File 'app/models/line_item.rb', line 425

def coupon_amount
  line_discounts.sum(:amount)
end

#credit_memoCreditMemo

Returns:

See Also:



104
# File 'app/models/line_item.rb', line 104

belongs_to :credit_memo, -> { joins(:line_items).where(line_items: { resource_type: 'CreditMemo' }) }, foreign_key: :resource_id, optional: true

#credit_order_line_itemLineItem

Returns:

See Also:



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

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

#credit_percentageObject



479
480
481
482
483
484
485
486
487
# File 'app/models/line_item.rb', line 479

def credit_percentage
  return nil if credit_rma_item.nil? && credit_order_line_item.nil?

  if credit_rma_item
    credit_rma_item.credit_percentage
  elsif credit_order_line_item.try(:credit_rma_item)
    credit_order_line_item.credit_rma_item.credit_percentage
  end
end

#credit_rma_itemRmaItem

Returns:

See Also:



110
# File 'app/models/line_item.rb', line 110

belongs_to :credit_rma_item, class_name: 'RmaItem', optional: true

#currencyObject

Delegators



421
422
423
# File 'app/models/line_item.rb', line 421

def currency
  resource.try(:currency)
end

#currency_symbolObject



640
641
642
# File 'app/models/line_item.rb', line 640

def currency_symbol
  (resource || delivery).currency_symbol
end

#current_line_cogsObject



731
732
733
734
735
736
737
738
739
740
741
742
743
744
# File 'app/models/line_item.rb', line 731

def current_line_cogs
  # Catalog cogs first
  uc = unit_cogs if unit_cogs && unit_cogs > 0
  uc ||= catalog_item.try(:unit_cogs)
  uc ||= begin
    SupplierItem.order(:id).reverse_order.find_by(item_id:).current_price.unit_cost * cogs_markup
  rescue StandardError
    nil
  end

  return if uc.nil?

  uc * quantity
end

#current_line_profitObject



758
759
760
761
762
763
764
# File 'app/models/line_item.rb', line 758

def current_line_profit
  if estimated_line_cost && (dt = discounted_total)
    dt - estimated_line_cost
  else
    BigDecimal('0.00')
  end
end

#current_line_profit_marginObject

To find the margin, divide gross profit by the revenue.



776
777
778
779
780
781
782
783
# File 'app/models/line_item.rb', line 776

def current_line_profit_margin
  gross_profit = current_line_profit
  revenue = discounted_total
  return 0.0 if gross_profit == 0
  return -1.0 if revenue == 0

  gross_profit / revenue
end

#current_line_profit_markupObject

To write the markup as a percentage, divide the gross profit by the COGS.



767
768
769
770
771
772
773
# File 'app/models/line_item.rb', line 767

def current_line_profit_markup
  cogs = estimated_line_cost
  gross_profit = current_line_profit
  return 0.0 if cogs == 0

  gross_profit / cogs
end

#deep_dupObject



87
88
89
# File 'app/models/line_item.rb', line 87

def deep_dup
  deep_clone(include: %i[serial_numbers line_discounts])
end

#deliveryDelivery

Returns:

See Also:



107
# File 'app/models/line_item.rb', line 107

belongs_to :delivery, inverse_of: :line_items, optional: true

#detail_urlObject



918
919
920
921
922
923
924
# File 'app/models/line_item.rb', line 918

def detail_url
  i = item
  return unless i&.canonical_path

  locale = resource.try(:store_id) == 2 ? 'en-CA' : 'en-US'
  "https://#{WEB_HOSTNAME}#{i.canonical_url(locale: locale)}"
end

#direct_store_itemStoreItem

Returns:

See Also:



106
# File 'app/models/line_item.rb', line 106

belongs_to :direct_store_item, class_name: 'StoreItem', foreign_key: :store_item_id, optional: true

#discounted_totalObject



644
645
646
647
648
649
650
651
652
653
# File 'app/models/line_item.rb', line 644

def discounted_total
  # ALL itemizables: use line_discounts for exact totals (prevents rounding errors)
  # For example: -$50.00 / 3 qty = -$16.67 per unit, but -16.67 × 3 = -50.01 ❌
  # Using line_discounts ensures: price × qty + line_discounts = exact total ✓
  # This keeps Ruby calculations in sync with SQL calculate_itemizable_total function
  #
  # Use Ruby sum (map + sum) instead of ActiveRecord sum(:column) to leverage
  # preloaded line_discounts and avoid N+1 SUM queries during batch operations.
  price_total + line_discounts.sum(&:amount)
end

#discountsActiveRecord::Relation<Discount>

Returns:

See Also:



126
# File 'app/models/line_item.rb', line 126

has_many :discounts, through: :line_discounts

#discounts_totalObject



661
662
663
# File 'app/models/line_item.rb', line 661

def discounts_total
  price_total - discounted_total
end

#dropship?Boolean

Returns:

  • (Boolean)


533
534
535
# File 'app/models/line_item.rb', line 533

def dropship?
  item.present? && item.dropship?
end

#effective_discountObject



687
688
689
690
# File 'app/models/line_item.rb', line 687

def effective_discount
  d = price_total - discounted_total
  price_total.positive? && d.positive? ? ((d * 100) / price_total).to_i : 0
end

#estimated_line_costObject



750
751
752
753
754
755
756
# File 'app/models/line_item.rb', line 750

def estimated_line_cost
  if is_shipping?
    price
  else
    current_line_cogs
  end
end

#fulfillment_origin_addressObject



821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
# File 'app/models/line_item.rb', line 821

def fulfillment_origin_address
  # TBD we need to properly populate this at store_item level, for drop shipping from a supplier with multiple addresses, e.g. HeatLay or HeatTrack  or WY office vs WY warehouse, etc.
  if is_service?
    if is_remote_service?
      res = resource.store.warehouse_service_address
    else
      res = delivery&.destination_address
      res ||= resource&.shipping_address
    end
  elsif is_shipping?
    res = delivery.origin_address if delivery
    res ||= resource.store.warehouse_address
    # logger.info "line item, fulfillment_origin_address: #{self.name}, shipping line, fulfillment_origin_address.id: #{res.id}"
  else
    res = store_item&.store&.warehouse_address
  end
  if item.dropship
    res = item.supplier_item.supplier.shipping_address
    # puts "line item: #{self.name}, dropship, fulfillment_origin_address.id: #{res.id}"
  end
  # puts "line item: #{self.name}, fulfillment_origin_address.id: #{res.id rescue NONE}"
  res
end

#fulfillment_origin_address_idObject



817
818
819
# File 'app/models/line_item.rb', line 817

def fulfillment_origin_address_id
  fulfillment_origin_address&.id
end

#get_item_idObject



529
530
531
# File 'app/models/line_item.rb', line 529

def get_item_id
  item&.id
end

#handling_chargeObject



794
795
796
# File 'app/models/line_item.rb', line 794

def handling_charge
  catalog_item&.store_item&.handling_charge
end

#has_children?Boolean

Returns:

  • (Boolean)


624
625
626
# File 'app/models/line_item.rb', line 624

def has_children?
  children_count.present? && (children_count > 0)
end

#has_linked_unvoided_rma_item?Boolean

Returns:

  • (Boolean)


461
462
463
# File 'app/models/line_item.rb', line 461

def has_linked_unvoided_rma_item?
  credit_rma_item.present? && !credit_rma_item.voided?
end

#image_pathObject

Alias for Item#image_path

Returns:

  • (Object)

    Item#image_path

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#image_urlObject

Alias for Item#image_url

Returns:

  • (Object)

    Item#image_url

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#in_stock?Boolean

Returns:

  • (Boolean)


567
568
569
570
571
572
573
# File 'app/models/line_item.rb', line 567

def in_stock?
  if catalog_item # coupons don't have the same data as line items
    catalog_item.qty_available >= quantity
  else
    true
  end
end

#inventory_commitsActiveRecord::Relation<InventoryCommit>

Returns:

See Also:



127
# File 'app/models/line_item.rb', line 127

has_many :inventory_commits, dependent: :destroy

#invoiceInvoice

Returns:

See Also:



103
# File 'app/models/line_item.rb', line 103

belongs_to :invoice, -> { joins(:line_items).where(line_items: { resource_type: 'Invoice' }) }, foreign_key: :resource_id, optional: true

#is_cork?Object

Alias for Item#is_cork?

Returns:

  • (Object)

    Item#is_cork?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_discounted?Boolean

Returns:

  • (Boolean)


692
693
694
# File 'app/models/line_item.rb', line 692

def is_discounted?
  effective_discount > 0
end

#is_floor_heating_product?Object

Alias for Item#is_floor_heating_product?

Returns:

  • (Object)

    Item#is_floor_heating_product?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_fulfillable?Boolean

Returns:

  • (Boolean)


616
617
618
# File 'app/models/line_item.rb', line 616

def is_fulfillable?
  %i[ok low na].include?(stock_status)
end

#is_goods?Boolean

Returns:

  • (Boolean)


863
864
865
# File 'app/models/line_item.rb', line 863

def is_goods?
  tax_class.present? ? tax_class == 'g' : item&.is_goods?&.to_b
end

#is_kit?Boolean

Returns:

  • (Boolean)


451
452
453
# File 'app/models/line_item.rb', line 451

def is_kit?
  item&.is_kit?
end

#is_legacy_relay?Object

Alias for Item#is_legacy_relay?

Returns:

  • (Object)

    Item#is_legacy_relay?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_linked_to_same_resource_as_deliveryObject



445
446
447
448
449
# File 'app/models/line_item.rb', line 445

def is_linked_to_same_resource_as_delivery
  return unless delivery&.resource && resource&.persisted? && delivery&.resource&.persisted? && resource.id != delivery.resource.id

  errors.add(:resource_id, 'is not the same as Delivery resource')
end

#is_not_a_kitObject



455
456
457
458
459
# File 'app/models/line_item.rb', line 455

def is_not_a_kit
  return unless is_kit?

  errors.add(:catalog_item_id, "#{sku} is a kit. Kits are not permitted on store transfers, please add kit components instead")
end

#is_oj_programmable_tstat?Object

Alias for Item#is_oj_programmable_tstat?

Returns:

  • (Object)

    Item#is_oj_programmable_tstat?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_onsite_service?Boolean

Returns:

  • (Boolean)


879
880
881
# File 'app/models/line_item.rb', line 879

def is_onsite_service?
  is_service? && item.sku.include?('ONSITE')
end

#is_publication?Object

Alias for Item#is_publication?

Returns:

  • (Object)

    Item#is_publication?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

Returns:

  • (Boolean)


899
900
901
902
903
904
905
906
907
908
# File 'app/models/line_item.rb', line 899

def is_related_smart_service?(sku)
  res = false
  # check if our SKU is the corresponding _PER_SQFT or _FIXRATE to the input SKU
  if sku.index('_FIXRATE') && is_smartinstall_service? && item.sku.index('_PER_SQFT') && item.sku.split('_PER_SQFT').first == sku.split('_FIXRATE')
    res = true
  elsif sku.index('_PER_SQFT') && is_smartinstall_service? && item.sku.index('_FIXRATE') && item.sku.split('_FIXRATE').first == sku.split('_PER_SQFT')
    res = true
  end
  res
end

#is_relay_panel?Object

Alias for Item#is_relay_panel?

Returns:

  • (Object)

    Item#is_relay_panel?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_remote_service?Boolean

Returns:

  • (Boolean)


875
876
877
# File 'app/models/line_item.rb', line 875

def is_remote_service?
  (is_service? && item.sku.include?('REMOTE')) || (is_service? && resource.line_items.any? { |a| a.sku.include?('REMOTE') })
end

#is_reviewable?Object

Alias for Item#is_reviewable?

Returns:

  • (Object)

    Item#is_reviewable?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_service?Boolean

Returns:

  • (Boolean)


871
872
873
# File 'app/models/line_item.rb', line 871

def is_service?
  tax_class.present? ? tax_class == 'svc' : item&.is_service?&.to_b
end

#is_shipping?Boolean

Returns:

  • (Boolean)


887
888
889
# File 'app/models/line_item.rb', line 887

def is_shipping?
  tax_class.present? ? tax_class == 'shp' : item&.is_shipping?&.to_b
end

#is_smart_service?Boolean

Returns:

  • (Boolean)


895
896
897
# File 'app/models/line_item.rb', line 895

def is_smart_service?
  item&.pl_descendant_of_path?(LtreePaths::PL_SERVICES)
end

#is_smartinstall_service?Boolean

Returns:

  • (Boolean)


891
892
893
# File 'app/models/line_item.rb', line 891

def is_smartinstall_service?
  item&.pl_descendant_of_path?(LtreePaths::PL_SERVICES_SMARTINSTALL)
end

#is_smartstat?Object

Alias for Item#is_smartstat?

Returns:

  • (Object)

    Item#is_smartstat?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_snow_melting_product?Object

Alias for Item#is_snow_melting_product?

Returns:

  • (Object)

    Item#is_snow_melting_product?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#is_spare_parts?Boolean

Returns:

  • (Boolean)


867
868
869
# File 'app/models/line_item.rb', line 867

def is_spare_parts?
  item&.is_spare_parts?&.to_b
end

#is_tax_unclassed?Boolean

Returns:

  • (Boolean)


883
884
885
# File 'app/models/line_item.rb', line 883

def is_tax_unclassed?
  tax_class == 'none'
end

#itemItem

Returns:

See Also:



113
# File 'app/models/line_item.rb', line 113

belongs_to :item, optional: true

#item_in_stock_in_storeObject (protected)



932
933
934
935
936
937
938
939
940
941
# File 'app/models/line_item.rb', line 932

def item_in_stock_in_store
  return unless get_item_id && resource&.store_id

  store_item = StoreItem.available.where(item_id: get_item_id, store_id: resource.store_id).first
  if store_item.nil?
    errors.add(:item_id, 'is not available in selected store')
  elsif quantity && (store_item.qty_available < quantity)
    errors.add(:quantity, 'is more than quantity available in selected store')
  end
end

#ledger_company_accountLedgerCompanyAccount



114
# File 'app/models/line_item.rb', line 114

belongs_to :ledger_company_account, optional: true

#ledger_descriptionObject



521
522
523
524
525
526
527
# File 'app/models/line_item.rb', line 521

def ledger_description
  if cm_category.nil? || ((cm_category == 'Item') && sku.present?)
    sku
  else
    "#{cm_category} #{description}"
  end
end

#line_discountsActiveRecord::Relation<LineDiscount>

Returns:

See Also:



125
# File 'app/models/line_item.rb', line 125

has_many :line_discounts, dependent: :destroy, inverse_of: :line_item, autosave: true

#line_locked?Boolean

Returns:

  • (Boolean)


537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
# File 'app/models/line_item.rb', line 537

def line_locked?
  return true if resource.respond_to?(:editing_locked?) && resource.editing_locked?
  # If an item is not plan sensitive, we don't care
  return false unless item.plan_sensitive?

  # If line item is linked to a room within a resource, we do a sanity check to ensure that
  # room is also part of the resource's room configurations
  return false unless room_configuration
  # If the line item is linked to a room not in the resource, don't lock it and do as you please
  return false if resource.respond_to?(:room_configuration_ids) && resource.room_configuration_ids.exclude?(room_configuration.id)

  # Otherwise the room dictates the locking status of that item
  room_configuration.editing_locked?

  # Not relevant edit as you wish
end

#line_total_with_taxObject



655
656
657
658
659
# File 'app/models/line_item.rb', line 655

def line_total_with_tax
  discounted_total + (tax_total || 0.0)
rescue StandardError
  0
end

#linear_ft_totalObject



683
684
685
# File 'app/models/line_item.rb', line 683

def linear_ft_total
  quantity * (item.length.to_f / 12)
end

#linked_to_credit_memoObject



475
476
477
# File 'app/models/line_item.rb', line 475

def linked_to_credit_memo
  resource_type == 'CreditMemo'
end

#linked_to_invoiceObject



489
490
491
# File 'app/models/line_item.rb', line 489

def linked_to_invoice
  resource_type == 'Invoice'
end

#linked_to_st_orderObject



505
506
507
# File 'app/models/line_item.rb', line 505

def linked_to_st_order
  (resource_type == 'Order') && (resource.try(:order_type) == 'ST')
end

#linked_to_standalone_credit_memoObject



493
494
495
# File 'app/models/line_item.rb', line 493

def linked_to_standalone_credit_memo
  (resource_type == 'CreditMemo') && (resource.try(:category) == 'standalone')
end

#linked_to_standalone_credit_memo_or_invoiceObject



501
502
503
# File 'app/models/line_item.rb', line 501

def linked_to_standalone_credit_memo_or_invoice
  linked_to_standalone_credit_memo || linked_to_standalone_invoice
end

#linked_to_standalone_invoiceObject



497
498
499
# File 'app/models/line_item.rb', line 497

def linked_to_standalone_invoice
  (resource_type == 'Invoice') && [Invoice::MI].include?(resource.try(:invoice_type))
end

#max_discountObject

Determine the max discount in effect for this line item
Only for quote or order



344
345
346
347
348
349
350
351
# File 'app/models/line_item.rb', line 344

def max_discount
  md = nil
  if (discountable_resource = quote || order)
    md = discountable_resource.max_discount_override
  end
  md = catalog_item.max_discount if md.nil? && catalog_item
  md
end

#maximum_currentObject

Alias for Item#maximum_current

Returns:

  • (Object)

    Item#maximum_current

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#msrpObject

Alias for Catalog_item#msrp

Returns:

  • (Object)

    Catalog_item#msrp

See Also:



248
249
250
251
252
253
254
# File 'app/models/line_item.rb', line 248

delegate :price_with_vat,
:sale_price_with_vat,
:msrp,
:msrp_with_vat,
:root_catalog_item,
to: :catalog_item,
allow_nil: true

#msrp_totalObject



671
672
673
674
675
676
677
# File 'app/models/line_item.rb', line 671

def msrp_total
  return 0.0 if parent_id.present?

  (msrp || 0.0) * quantity
rescue StandardError
  0.0
end

#msrp_with_vatObject

Alias for Catalog_item#msrp_with_vat

Returns:

  • (Object)

    Catalog_item#msrp_with_vat

See Also:



248
249
250
251
252
253
254
# File 'app/models/line_item.rb', line 248

delegate :price_with_vat,
:sale_price_with_vat,
:msrp,
:msrp_with_vat,
:root_catalog_item,
to: :catalog_item,
allow_nil: true

#nameObject



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

def name
  description || item.try(:name) || item_name
end

#num_polesObject

Alias for Item#num_poles

Returns:

  • (Object)

    Item#num_poles

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#orderOrder

The following makes join easier

Returns:

See Also:



101
# File 'app/models/line_item.rb', line 101

belongs_to :order, -> { joins(:line_items).where(line_items: { resource_type: 'Order' }) }, foreign_key: :resource_id, optional: true

#oversize?Object

Alias for Item#oversize?

Returns:

  • (Object)

    Item#oversize?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#parent_quantity_factorObject

Determines the parent line quantity



354
355
356
357
358
359
360
361
362
363
364
365
# File 'app/models/line_item.rb', line 354

def parent_quantity_factor
  return unless parent&.quantity

  # what type of item is the parent
  # parent_item = parent.item
  # how many of this component item are normally in the parent item
  # tir = parent_item.kit_target_item_relations.where(target_item_id: item_id).first
  # component_quantity = tir.quantity
  (quantity / parent.quantity).to_i
  # How many quantity in this line item
  # component_quantity
end

#preset_jobPresetJob

Returns:

See Also:



121
# File 'app/models/line_item.rb', line 121

has_one :preset_job, dependent: :destroy

#price_editable?(account = nil) ⇒ Boolean

Returns:

  • (Boolean)


803
804
805
806
807
808
809
# File 'app/models/line_item.rb', line 803

def price_editable?( = nil)
  .has_role?('sales_manager') || .has_role?('admin') || (begin
    catalog_item.price_editable
  rescue StandardError
    false
  end)
end

#price_totalObject



665
666
667
668
669
# File 'app/models/line_item.rb', line 665

def price_total
  (price || 0.0) * quantity
rescue StandardError
  0.0
end

#price_with_vatObject

Alias for Catalog_item#price_with_vat

Returns:

  • (Object)

    Catalog_item#price_with_vat

See Also:



248
249
250
251
252
253
254
# File 'app/models/line_item.rb', line 248

delegate :price_with_vat,
:sale_price_with_vat,
:msrp,
:msrp_with_vat,
:root_catalog_item,
to: :catalog_item,
allow_nil: true

#product_line_for_reviewObject

Alias for Item#product_line_for_review

Returns:

  • (Object)

    Item#product_line_for_review

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#product_tax_codeObject

Alias for Item#product_tax_code

Returns:

  • (Object)

    Item#product_tax_code

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#purchase_order_itemPurchaseOrderItem



120
# File 'app/models/line_item.rb', line 120

has_one :purchase_order_item, dependent: :restrict_with_error

#quantities_indicator_visible?Boolean

Returns:

  • (Boolean)


811
812
813
814
815
# File 'app/models/line_item.rb', line 811

def quantities_indicator_visible?
  item.quantities_indicator_visible?
rescue StandardError
  true
end

#quoteQuote

Returns:

See Also:



102
# File 'app/models/line_item.rb', line 102

belongs_to :quote, -> { joins(:line_items).where(line_items: { resource_type: 'Quote' }) }, foreign_key: :resource_id, optional: true

#remaining_quantity_to_allocate_to_shipmentsObject



433
434
435
# File 'app/models/line_item.rb', line 433

def remaining_quantity_to_allocate_to_shipments
  quantity - shipment_contents.sum(:quantity)
end

#replacement_rma_itemRmaItem

Returns:

See Also:



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

belongs_to :replacement_rma_item, class_name: 'RmaItem', optional: true

#reported_category_nameObject



914
915
916
# File 'app/models/line_item.rb', line 914

def reported_category_name
  item&.primary_product_line&.lineage_expanded || 'unknown'
end

#require_serial_number?Boolean

Returns:

  • (Boolean)


798
799
800
801
# File 'app/models/line_item.rb', line 798

def require_serial_number?
  false
  # catalog_item.store_item.requires_serial_number? rescue false
end

#resourceResource

Returns:

  • (Resource)

See Also:



99
# File 'app/models/line_item.rb', line 99

belongs_to :resource, polymorphic: true, inverse_of: :line_items, optional: true

#reviewReview

Returns:

See Also:



122
# File 'app/models/line_item.rb', line 122

has_one :review, dependent: :restrict_with_error

#rma_itemsActiveRecord::Relation<RmaItem>

Returns:

  • (ActiveRecord::Relation<RmaItem>)

See Also:



124
# File 'app/models/line_item.rb', line 124

has_many :rma_items, foreign_key: 'returned_line_item_id', dependent: :destroy, inverse_of: :returned_line_item

#room_configurationRoomConfiguration

Returns:

  • (RoomConfiguration)

See Also:



109
# File 'app/models/line_item.rb', line 109

belongs_to :room_configuration, optional: true

#root_catalog_itemObject

Alias for Catalog_item#root_catalog_item

Returns:

  • (Object)

    Catalog_item#root_catalog_item

See Also:



248
249
250
251
252
253
254
# File 'app/models/line_item.rb', line 248

delegate :price_with_vat,
:sale_price_with_vat,
:msrp,
:msrp_with_vat,
:root_catalog_item,
to: :catalog_item,
allow_nil: true

#sale_price_with_vatObject

Alias for Catalog_item#sale_price_with_vat

Returns:

  • (Object)

    Catalog_item#sale_price_with_vat

See Also:



248
249
250
251
252
253
254
# File 'app/models/line_item.rb', line 248

delegate :price_with_vat,
:sale_price_with_vat,
:msrp,
:msrp_with_vat,
:root_catalog_item,
to: :catalog_item,
allow_nil: true

#serial_numberSerialNumber



108
# File 'app/models/line_item.rb', line 108

belongs_to :serial_number, optional: true

#set_cogsObject (protected)



943
944
945
946
947
948
949
950
951
# File 'app/models/line_item.rb', line 943

def set_cogs
  return unless !quantity.nil? && unit_cogs.nil? && resource.store_id.present?

  store_item = StoreItem.available.where(item_id: get_item_id, store_id: resource.store_id).first
  return if store_item.nil?

  self.unit_cogs = store_item.unit_cogs
  self.total_cogs = store_item.unit_cogs * quantity
end

#set_cogs_for_store_transferObject (protected)



953
954
955
956
957
958
959
960
961
962
# File 'app/models/line_item.rb', line 953

def set_cogs_for_store_transfer
  return unless resource.try(:is_store_transfer?)
  return if unit_cogs.present? || quantity.nil?

  store_item = self.store_item
  return if store_item.nil?

  self.unit_cogs = store_item.unit_cogs
  self.total_cogs = store_item.unit_cogs * quantity if store_item.unit_cogs.present?
end

#shipment_contentsActiveRecord::Relation<ShipmentContent>

Returns:

See Also:



128
# File 'app/models/line_item.rb', line 128

has_many :shipment_contents, dependent: :destroy

#shipping_costShippingCost



116
# File 'app/models/line_item.rb', line 116

belongs_to :shipping_cost, optional: true

#shipping_nameObject



910
911
912
# File 'app/models/line_item.rb', line 910

def shipping_name
  delivery&.retrieve_shipping_description_for_shipping_cost(shipping_cost) || name
end

#shipping_weightObject



785
786
787
788
# File 'app/models/line_item.rb', line 785

def shipping_weight
  # important: item.base_weight is the better quality number and means Net Unit Weight so the correct number to use when calculating item weight because tare weight (of the box, pallet, crate etc) is added after
  item&.base_weight || 0.0
end

#ships_via_freight?Object

Alias for Item#ships_via_freight?

Returns:

  • (Object)

    Item#ships_via_freight?

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#show_out_of_stock?(item_availability = {}) ⇒ Boolean

Returns:

  • (Boolean)


575
576
577
578
579
580
581
582
583
584
585
# File 'app/models/line_item.rb', line 575

def show_out_of_stock?(item_availability = {})
  if dropship? && resource.respond_to?(:single_origin?) && resource.single_origin?
    !in_stock?
  elsif dropship? || item.try(:always_available_online?)
    false
  elsif !item_availability.empty? && item_availability[catalog_item_id].present?
    !item_availability[catalog_item_id]['in_stock']
  else
    !in_stock?
  end
end

#single_origin_or_warehouse_pickup?Boolean

Returns:

  • (Boolean)


587
588
589
590
591
# File 'app/models/line_item.rb', line 587

def single_origin_or_warehouse_pickup?
  return false if resource.is_a?(RoomConfiguration)

  resource.single_origin? || resource.is_warehouse_pickup?
end

#skuObject



509
510
511
# File 'app/models/line_item.rb', line 509

def sku
  item&.sku || sku_override
end

#sort_priorityObject



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'app/models/line_item.rb', line 376

def sort_priority
  i = item || catalog_item&.store_item&.item
  return [0, 0, 0, 0] unless i

  s1 = i.product_category_priority
  if i.is_heating_element?
    s2 = -begin
      i.width.to_i
    rescue StandardError
      0
    end
    s3 = -begin
      i.length.to_i
    rescue StandardError
      0
    end
  else
    s2 = 0
    s3 = 0
  end
  s4 = i.sku
  [s1, s2, s3, s4]
end

#sq_ft_totalObject



679
680
681
# File 'app/models/line_item.rb', line 679

def sq_ft_total
  quantity * item.sqft.to_f
end

#stock_statusObject



593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
# File 'app/models/line_item.rb', line 593

def stock_status
  return :none unless catalog_item
  return :na if quantity < 0 # not applicable for credit orders
  return :ok if dropship? && !single_origin_or_warehouse_pickup?
  return :ok if unlimited_inventory?

  # Check if this specific line item has enough commits to fulfill its quantity.
  # Use Enumerable#sum (block form) so preloaded inventory_commits are used in-memory
  # without firing a SQL SUM aggregate per line item.
  committed_quantity = inventory_commits.sum(&:quantity)
  return :ok if committed_quantity >= quantity

  si = resource.respond_to?(:from_store_id) && resource.from_store_id ? StoreItem.where(store_id: resource.from_store_id, item_id:, location: 'AVAILABLE').first : store_item
  return :oos if si.qty_available < quantity
  return :low if item.qty_warn_on_stock && (si.qty_available <= item.qty_warn_on_stock)

  :ok
end

#store_itemObject



620
621
622
# File 'app/models/line_item.rb', line 620

def store_item
  direct_store_item || catalog_item&.store_item
end

#supplier_skusObject



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

def supplier_skus
  item&.supplier_skus || []
end

#taxable_totalObject



696
697
698
699
700
701
702
# File 'app/models/line_item.rb', line 696

def taxable_total
  if invoice&.invoice_type == Invoice::CI
    discounted_total
  else
    taxable_amount.present? ? (taxable_amount * quantity) : discounted_total
  end
end

#taxes_grouped_by_typeObject



367
368
369
370
371
372
373
374
# File 'app/models/line_item.rb', line 367

def taxes_grouped_by_type
  tax_grouped = {}
  if tax_rate.present? && tax_type.present?
    rate_percentage = (tax_rate * 100)
    tax_grouped[tax_type] = { tax_amount: tax_total, name: "#{TaxRate.description_for(tax_type)} #{rate_percentage}%", rate: rate_percentage }
  end
  tax_grouped
end

#thumbnail_urlObject

Alias for Item#thumbnail_url

Returns:

  • (Object)

    Item#thumbnail_url

See Also:



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/line_item.rb', line 226

delegate :is_oj_programmable_tstat?,
:is_smartstat?,
:is_cork?,
:is_relay_panel?,
:is_legacy_relay?,
:is_publication?,
:maximum_current,
:num_poles,
:category_name,
:image_path,
:image_url,
:thumbnail_url,
:is_floor_heating_product?,
:oversize?,
:ships_via_freight?,
:product_tax_code,
:is_snow_melting_product?,
:is_reviewable?,
:product_line_for_review,
to: :item,
allow_nil: true

#totalObject



704
705
706
# File 'app/models/line_item.rb', line 704

def total
  discounted_total
end

#total_shipping_weightObject



790
791
792
# File 'app/models/line_item.rb', line 790

def total_shipping_weight
  (shipping_weight * quantity)
end

#unit_supplier_purchase_costObject

def total=(value)
end



711
712
713
# File 'app/models/line_item.rb', line 711

def unit_supplier_purchase_cost
  item.try(:supplier_items)&.last&.current_price&.purchasing_cost
end

#unit_value_for_commercial_invoiceObject



715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
# File 'app/models/line_item.rb', line 715

def unit_value_for_commercial_invoice
  is_intra_company_or_electronic_ci_st = delivery&.order&.is_store_transfer? && (delivery&.order&.shipping_address&.is_warehouse_by_distance? || delivery&.electronic_ship_ci_pdf.present? || delivery&.should_have_electronic_commercial_invoice?) # test that we should use minimized customs value when not visible to end customer, that is, an intra-company ST or an ST shipped with electronic CI
  min_unit_value_arr = []
  min_unit_value_arr << discounted_price if discounted_price&.positive? # skip $0 here
  min_unit_value_arr << unit_supplier_purchase_cost if unit_supplier_purchase_cost&.positive? # skip $0 here
  min_unit_value_arr << unit_cogs if unit_cogs&.positive? # skip $0 here
  min_unit_value = min_unit_value_arr.min # now minimun will choose minimum of these three non-zero unit costs
  effective_price = discounted_price
  effective_price = unit_cogs unless effective_price&.positive? # have a fallback
  effective_price = unit_supplier_purchase_cost unless effective_price&.positive? # have a fallback
  effective_price = price unless effective_price&.positive? # have a fallback
  # For intra-company store transfers, use min_unit_value if available, otherwise fall back to effective_price
  # This handles cases where all three values (discounted_price, unit_supplier_purchase_cost, unit_cogs) are nil or not positive
  (is_intra_company_or_electronic_ci_st ? (min_unit_value || effective_price) : effective_price)
end

#unlimited_inventory?Boolean

Returns:

  • (Boolean)


612
613
614
# File 'app/models/line_item.rb', line 612

def unlimited_inventory?
  store_item&.unlimited_inventory?
end

#validate_quantity_limitObject



437
438
439
440
441
442
443
# File 'app/models/line_item.rb', line 437

def validate_quantity_limit
  return unless quantity && (ql = store_item&.quantity_limit)

  return unless quantity > ql

  errors.add(:quantity, "quantity limit #{ql} exceeded for this item")
end

#voidObject



845
846
847
848
849
850
851
852
853
854
855
856
857
# File 'app/models/line_item.rb', line 845

def void
  puts 'line_item.void'
  if resource.line_items.goods.length == 1
    # here we do an RMA cancel since this is the only time that this is called.
    resource.rma_cancel
  else
    order = resource
    destroy
    # try to mark the credit order as returned
    order.reload
    order.returned
  end
end