Class: Dscf::Marketplace::AggregatorListing

Inherits:
ApplicationRecord show all
Defined in:
app/models/dscf/marketplace/aggregator_listing.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.ransackable_associations(_auth_object = nil) ⇒ Object



124
125
126
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 124

def self.ransackable_associations(_auth_object = nil)
  %w[aggregator supplier_product product sub_supplier_product]
end

.ransackable_attributes(_auth_object = nil) ⇒ Object



119
120
121
122
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 119

def self.ransackable_attributes(_auth_object = nil)
  %w[id aggregator_id supplier_product_id product_id sub_supplier_product_id source_kind price quantity status created_at updated_at] +
    %w[product_name product_description product_category product_category_id]
end

.resolved_product_column_sql(column) ⇒ Object

Pulls one column off the resolved product row.



91
92
93
94
95
96
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 91

def self.resolved_product_column_sql(column)
  <<~SQL.squish
    (SELECT p.#{column} FROM #{Product.table_name} p
     WHERE p.id = (#{resolved_product_id_sql}))
  SQL
end

.resolved_product_id_sqlObject

Resolves the catalogue product id in SQL, mirroring #product: which column holds the reference depends on source_kind, so a plain association join can't reach it. Used by the ransackers below so retailers can filter the feed on product attributes regardless of how a listing was sourced.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 74

def self.resolved_product_id_sql
  <<~SQL.squish
    CASE #{table_name}.source_kind
      WHEN #{source_kinds[:supplier]} THEN (
        SELECT sp.product_id FROM #{SupplierProduct.table_name} sp
        WHERE sp.id = #{table_name}.supplier_product_id
      )
      WHEN #{source_kinds[:sub_supplier]} THEN (
        SELECT ssp.product_id FROM #{SubSupplierProduct.table_name} ssp
        WHERE ssp.id = #{table_name}.sub_supplier_product_id
      )
      ELSE #{table_name}.product_id
    END
  SQL
end

Instance Method Details

#available?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 198

def available?
  active? && quantity.to_i.positive? && upstream_active?
end

#confirming_businessObject

The real external business that should confirm an order line placed against this listing on their OWN business dashboard, if any. Only meaningful for :supplier — that business is a genuine third party, unambiguously distinct from the aggregator. :catalog has no external party (the aggregator fulfils it) and :sub_supplier's business is offline/has no login (requirements doc: sub-suppliers "don't act directly on the system"), so both stay aggregator-managed (nil here).



172
173
174
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 172

def confirming_business
  supplier_product&.business if supplier?
end

#cost_priceObject

The aggregator's cost basis for this listing (nil for catalog — the aggregator sets a selling price directly with no upstream cost to compare against).



139
140
141
142
143
144
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 139

def cost_price
  case source_kind
  when "supplier" then supplier_product&.supplier_price
  when "sub_supplier" then sub_supplier_product&.sub_supplier_price
  end
end

#effective_statusObject

Status as retailers/aggregator should see it: a listing whose own status is :active but whose supplier has paused reads as :paused (auto-followed), without mutating the stored status.



192
193
194
195
196
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 192

def effective_status
  return "paused" if active? && !upstream_active?

  status
end

#marginObject



146
147
148
149
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 146

def margin
  return nil unless cost_price
  price - cost_price
end

#productObject

Resolves the catalogue product regardless of how the listing was sourced.



129
130
131
132
133
134
135
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 129

def product
  case source_kind
  when "supplier" then supplier_product&.product
  when "sub_supplier" then sub_supplier_product&.product
  else super
  end
end

#source_labelObject

Human-readable name of who ultimately fulfils a line placed against this listing. An order line is auto-sourced to the AggregatorListing itself, so this drives the "Product source name" the requirements ask for:

catalog      -> the aggregator (self-fulfilled)
supplier     -> the supplier's business name
sub_supplier -> the sub-supplier's business name


157
158
159
160
161
162
163
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 157

def source_label
  case source_kind
  when "supplier" then supplier_product&.business&.name
  when "sub_supplier" then sub_supplier_product&.sub_supplier&.business_name
  else aggregator&.name
  end
end

#total_valueObject



202
203
204
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 202

def total_value
  price * quantity
end

#upstream_active?Boolean

Is the upstream source still offering this? For supplier/sub_supplier sources, an aggregator listing must follow the supplier: when the supplier pauses (deactivates their supplier product) or runs out of stock, the listing is no longer offerable even if its own status is still :active. Catalog listings have no upstream — the aggregator fulfils them directly.

Returns:

  • (Boolean)


181
182
183
184
185
186
187
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 181

def upstream_active?
  case source_kind
  when "supplier" then supplier_product&.active? && supplier_product.available_quantity.to_i.positive?
  when "sub_supplier" then sub_supplier_product.present?
  else true
  end
end