Class: Pdf::Document::PriceList

Inherits:
Object
  • Object
show all
Includes:
ActionView::Helpers::NumberHelper, ActiveModel::API, ActiveModel::Attributes, ActiveModel::Validations, ActiveModel::Validations::Callbacks, Heatwave::AttributeNormalizing, Base, StripAttributes
Defined in:
app/services/pdf/document/price_list.rb

Overview

Service object: price list.

Constant Summary collapse

SECTIONS =

Sections.

{
  'Floor Heating': {
    fh_tempzone_flex_twin_120: 'TempZone Flex 120V',
    fh_tempzone_flex_twin_240: 'TempZone Flex 240V',
    fh_tempzone_cable_120: 'TempZone Cable 120V',
    fh_tempzone_cable_240: 'TempZone Cable 240V',
    fh_tempzone_ruler_cable_120: 'TempZone Ruler Cable 120V',
    fh_tempzone_ruler_cable_240: 'TempZone Ruler Cable 240V',
    fh_tempzone_easy_twin_120: 'TempZone Easy Mat 120V',
    fh_tempzone_shower_bench: 'TempZone Shower Bench Mat',
    fh_tempzone_shower_floor: 'TempZone Shower Floor Mat',
    fh_tempzone_custom: 'TempZone Custom Mat',
    fh_environ_flex_120: 'Environ Flex 120V',
    fh_environ_flex_240: 'Environ Flex 240V',
    fh_environ_easy_120: 'Environ Easy Mat 120V',
    fh_environ_easy_240: 'Environ Easy Mat 240V',
    fh_slab_cable: 'Slab Heating Cable',
    fh_slab_mat: 'Slab Heating Mat',
    fh_controls: 'Controls',
    fh_power_modules: 'Power Modules',
    fh_relay_panels: 'Relay Panels',
    fh_underlayment: 'Underlayment',
    fh_rough_in_kits: 'Rough-in Kits',
    fh_installation_kits: 'Installation Kits',
    fh_accessories: 'Accessories',
    fh_spare_parts: 'Spare Parts'
  },
  'Towel Warmers': {
    tw_classic_plugin: 'Classic Plug-in',
    tw_classic_hardwired: 'Classic Hardwired',
    tw_classic_dual: 'Classic Dual Connection (Plug-in or Hardwired)',
    tw_cosmopolitan_hardwired: 'Cosmopolitan Hardwired',
    tw_controls: 'Towel Warmer Controls'
  },
  'Infrared Heating Panels': {
    rp_lava_glass: 'Lava Glass',
    rp_lava_mirror: 'Lava Mirror',
    rp_lava_crystal: 'Lava Crystal',
    rp_lava_light: 'Lava Light',
    rp_lava_accessories: 'Lava Accessories',
    rp_ember_flex: 'Ember Flex',
    rp_ember_glass: 'Ember Glass',
    rp_ember_mirror: 'Ember Mirror',
    rp_ember_accessories: 'Ember Accessories',
    rp_controls: 'Controls'
  },
  Mirrors: {
    mirror_led_marquee: 'Marquee LED Mirrors',
    mirror_defoggers: 'Mirror Defoggers'
  },
  'FeelsWarm® Countertop Heating': {
    ch_standard: 'Standard Kit',
    ch_custom: 'Custom Kit',
    ch_controls: 'Controls'
  },
  'Shower Kits': {
    sk_shower_pan_kit: 'Shower Pan Kit',
    sk_drain_assembly_kit: 'Drain Assembly Kit',
    sk_drain_grate_covers: 'Drain Grate Covers',
    sk_shower_floor_heating_kits: 'Shower Floor Heating Kits',
    sk_accessories: 'Accessories',
    sk_drain_assemblies: 'Drain Assemblies'
  },
  'Snow Melting': {
    sm_mat_120: 'Mat 120V',
    sm_mat_240: 'Mat 240V',
    sm_cable_120: 'Cable 120V',
    sm_cable_240: 'Cable 240V',
    sm_cable_208: 'Cable 208V',
    sm_cable_277: 'Cable 277V',
    sm_controls: 'Controls',
    sm_sensors: 'Sensors',
    sm_relay_panels: 'Relay Panels',
    sm_accessories: 'Accessories'
  },
  'Roof & Gutter Deicing': {
    rgd_self_regulating_cable_120: 'Self Regulating Cable 120V',
    rgd_self_regulating_cable_240: 'Self Regulating Cable 240V',
    rgd_constant_wattage_kit: 'Constant Wattage Kit',
    rgd_controls: 'Controls',
    rgd_sensors: 'Sensors',
    rgd_relay_panels: 'Relay Panels',
    rgd_accessories: 'Accessories'
  },
  'Pipe Freeze Protection': {
    pfp_self_regulating_cable_120: 'Self Regulating Cable 120V',
    pfp_self_regulating_cable_240: 'Self Regulating Cable 240V',
    pfp_constant_wattage_cable: 'Constant Wattage Cable',
    pfp_controls: 'Controls',
    pfp_accessories: 'Accessories'
  }
}.freeze
SECTION_SPEC_COLUMNS =

Maps section key prefix → [extra spec symbols] shown between NAME and PRICE

{
  'fh_tempzone_flex'      => { extra: %i[width length coverage watts amps] },
  'fh_tempzone_cable'     => { extra: %i[width length coverage watts amps] },
  'fh_tempzone_ruler'     => { extra: %i[width length coverage watts amps] },
  'fh_tempzone_easy'      => { extra: %i[coverage watts amps] },
  'fh_tempzone_shower'    => { extra: %i[coverage watts] },
  'fh_tempzone_custom'    => { extra: %i[coverage watts] },
  'fh_environ_flex'       => { extra: %i[width length coverage watts amps] },
  'fh_environ_easy'       => { extra: %i[coverage watts amps] },
  'fh_slab'               => { extra: %i[length watts amps] },
  'fh_controls'           => { extra: %i[maximum_current], name_col: true },
  'fh_power_modules'      => { extra: %i[maximum_current], name_col: true },
  'fh_relay_panels'       => { name_col: true },
  'fh_underlayment'       => { extra: %i[width length], name_col: true },
  'fh_rough_in_kits'      => { name_col: true },
  'fh_installation_kits'  => { name_col: true },
  'fh_accessories'        => { name_col: true },
  'fh_spare_parts'        => { name_col: true },
  'tw_'                   => { extra: %i[finish width height depth], name_col: true, dimension_unit: 'in' },
  'rp_'                   => { name_col: true },
  'mirror_'               => { name_col: true },
  'ch_'                   => { name_col: true },
  'sk_'                   => { name_col: true },
  'sm_mat'                => { extra: %i[width length coverage watts amps] },
  'sm_cable'              => { extra: %i[length watts amps] },
  'sm_controls'           => { extra: %i[maximum_current], name_col: true },
  'sm_sensors'            => { name_col: true },
  'sm_relay_panels'       => { name_col: true },
  'sm_accessories'        => { name_col: true },
  'rgd_'                  => { name_col: true },
  'pfp_'                  => { name_col: true }
}.freeze
GROUP_HEADINGS =

Maps section key → group heading shown before first section in that family

{
  'fh_' => 'FLOOR HEATING',
  'tw_' => 'TOWEL WARMERS',
  'rp_' => 'INFRARED HEATING PANELS',
  'mirror_' => 'MIRRORS',
  'ch_' => "FEELSWARM\u00AE COUNTERTOP HEATING",
  'sk_' => 'SHOWER KITS',
  'sm_' => 'SNOW MELTING',
  'rgd_' => 'ROOF & GUTTER DEICING',
  'pfp_' => 'PIPE FREEZE PROTECTION'
}.freeze
DIMENSION_SPECS =

Dimension specs.

%i[width length height depth].freeze
DEFAULT_DIMENSION_UNIT =

Default unit that dimension columns (WIDTH/LENGTH/…) render in, unless a
section opts out via dimension_unit: in SECTION_SPEC_COLUMNS (towel
warmers keep inches for their finish/width/height/depth specs).

'ft'
SPEC_UNIT_LABELS =

Spec unit labels.

{
  'in' => 'in.', 'ft' => 'ft', 'mm' => 'mm', 'cm' => 'cm', 'm' => 'm'
}.freeze
FLAG_IMAGE_PATHS =

Catalogs that get a country flag in the ITEM # column. Any other catalog
surfaces its name in the header instead.

{
  'WarmlyYours-US' => Rails.public_path.join('images', 'pdf', 'flag-us.png').to_s,
  'WarmlyYours-CA' => Rails.public_path.join('images', 'pdf', 'flag-ca.png').to_s
}.freeze

Constants included from Base

Base::FONT, Base::NIMBUS_SANS_PATH, Base::NIMBUS_SANS_PATH_BOLD, Base::WY_LOGO_PATH

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Heatwave::AttributeNormalizing

normalize

Constructor Details

#initialize(options = {}) ⇒ PriceList

Returns a new instance of PriceList.



154
155
156
# File 'app/services/pdf/document/price_list.rb', line 154

def initialize(options = {})
  super
end

Instance Attribute Details

#rendererObject (readonly)

Returns the value of attribute renderer.



16
17
18
# File 'app/services/pdf/document/price_list.rb', line 16

def renderer
  @renderer
end

Class Method Details

.details_and_prices_for(catalog_id:, product_line_path: nil, product_category_path: nil, available_to_public_only: true, skus: [], sku_regex: nil, exact_product_category: false, item_condition: nil) ⇒ Object

Uses ltree path-based queries (no DB lookup needed)



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'app/services/pdf/document/price_list.rb', line 177

def self.details_and_prices_for(catalog_id:,
                                product_line_path: nil,
                                product_category_path: nil,
                                available_to_public_only: true,
                                skus: [],
                                sku_regex: nil,
                                exact_product_category: false,
                                item_condition: nil)
  if skus.any?
    sku_order_sql = skus.each_with_index.map { |sku, i| "WHEN '#{sku}' THEN #{i}" }.join(' ')
    items = Item.where(sku: skus).order(Arel.sql("CASE sku #{sku_order_sql} END"))
  elsif sku_regex
    items = Item.where('sku ~* ?', sku_regex).order(price: :asc, sku: :asc)
  else
    items = Item.by_product_line_path(product_line_path)
    if product_category_path.present?
      items = exact_product_category ? items.by_product_category_path_exact(product_category_path) : items.by_product_category_path(product_category_path)
    end
    items = items.available_to_public if available_to_public_only
    items = items.order(:price, :sku)
  end
  price_sql = "(select amount from catalog_items left join store_items on store_items.id = catalog_items.store_item_id left join items items2 on items2.id = store_items.item_id where store_items.location = 'AVAILABLE' and catalog_id = #{catalog_id} and items2.id = items.id)"
  items = items.select("items.*,#{price_sql} as price")
  items = items.where("#{price_sql} is not null")
  items = items.where(condition: item_condition) if item_condition.present?
  items
end

.item_conditions_for_selectObject



158
159
160
# File 'app/services/pdf/document/price_list.rb', line 158

def self.item_conditions_for_select
  [['Any', ''], ['New Only', 'new'], ['Refurb/B-stocks', 'refurbished']]
end

Instance Method Details

#callObject



205
206
207
208
209
210
211
212
213
214
# File 'app/services/pdf/document/price_list.rb', line 205

def call
  composer = build_composer
  draw_body(composer)
  insert_headers_and_footers(composer)
  io = StringIO.new
  composer.write(io, optimize: true)
  file_path = Rails.application.config.x.temp_storage_path.join(file_name)
  File.binwrite(file_path, io.string)
  Upload.uploadify(file_path, 'price_list_pdf', nil, file_name)
end

#catalogObject



220
221
222
223
224
# File 'app/services/pdf/document/price_list.rb', line 220

def catalog
  return @catalog if defined?(@catalog)

  @catalog = catalog_id && Catalog.find(catalog_id)
end

#details_and_prices_for(product_line_path:, product_category_path: nil, exact_product_category: false, available_to_public_only: true) ⇒ Object

Instance method delegates to class method



163
164
165
166
167
168
169
170
171
172
173
174
# File 'app/services/pdf/document/price_list.rb', line 163

def details_and_prices_for(product_line_path:, product_category_path: nil,
                           exact_product_category: false,
                           available_to_public_only: true)
  Pdf::Document::PriceList.details_and_prices_for(
    catalog_id: catalog.id,
    product_line_path:,
    product_category_path:,
    item_condition:,
    exact_product_category:,
    available_to_public_only:
  )
end

#file_nameObject



216
217
218
# File 'app/services/pdf/document/price_list.rb', line 216

def file_name
  'price_list.pdf'
end