Class: Dscf::Marketplace::AggregatorListing
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Dscf::Marketplace::AggregatorListing
- Defined in:
- app/models/dscf/marketplace/aggregator_listing.rb
Class Method Summary collapse
- .ransackable_associations(_auth_object = nil) ⇒ Object
- .ransackable_attributes(_auth_object = nil) ⇒ Object
-
.resolved_product_column_sql(column) ⇒ Object
Pulls one column off the resolved product row.
-
.resolved_product_id_sql ⇒ Object
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.
Instance Method Summary collapse
- #available? ⇒ Boolean
-
#confirming_business ⇒ Object
The real external business that should confirm an order line placed against this listing on their OWN business dashboard, if any.
-
#cost_price ⇒ Object
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).
-
#effective_status ⇒ Object
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.
- #margin ⇒ Object
-
#product ⇒ Object
Resolves the catalogue product regardless of how the listing was sourced.
-
#source_label ⇒ Object
Human-readable name of who ultimately fulfils a line placed against this listing.
- #total_value ⇒ Object
-
#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.
Class Method Details
.ransackable_associations(_auth_object = nil) ⇒ Object
129 130 131 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 129 def self.ransackable_associations(_auth_object = nil) %w[aggregator supplier_product product sub_supplier_product] end |
.ransackable_attributes(_auth_object = nil) ⇒ Object
124 125 126 127 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 124 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.
96 97 98 99 100 101 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 96 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_sql ⇒ Object
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
203 204 205 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 203 def available? active? && quantity.to_i.positive? && upstream_active? end |
#confirming_business ⇒ Object
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).
177 178 179 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 177 def confirming_business supplier_product&.business if supplier? end |
#cost_price ⇒ Object
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).
144 145 146 147 148 149 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 144 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_status ⇒ Object
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.
197 198 199 200 201 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 197 def effective_status return "paused" if active? && !upstream_active? status end |
#margin ⇒ Object
151 152 153 154 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 151 def margin return nil unless cost_price price - cost_price end |
#product ⇒ Object
Resolves the catalogue product regardless of how the listing was sourced.
134 135 136 137 138 139 140 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 134 def product case source_kind when "supplier" then supplier_product&.product when "sub_supplier" then sub_supplier_product&.product else super end end |
#source_label ⇒ Object
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
162 163 164 165 166 167 168 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 162 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_value ⇒ Object
207 208 209 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 207 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.
186 187 188 189 190 191 192 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 186 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 |