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
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_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
198 199 200 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 198 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).
172 173 174 |
# File 'app/models/dscf/marketplace/aggregator_listing.rb', line 172 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).
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_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.
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 |
#margin ⇒ Object
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 |
#product ⇒ Object
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_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
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_value ⇒ Object
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.
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 |