Class: CustomerOutletPurchaseQuery
- Inherits:
-
Object
- Object
- CustomerOutletPurchaseQuery
- Defined in:
- app/queries/customer_outlet_purchase_query.rb
Overview
Finds outlet purchases for review and reporting — the daily digest, the CRM
review queue, and ad-hoc questions like "what has accounting not signed off".
Composable scopes rather than one method per question, so a caller can chain
what it needs without this growing a method per combination.
Constant Summary collapse
- REVIEW_ASSOCIATIONS =
What every per-row review surface reads: the buyer (its name, and the two
source rows behind CustomerOutletPurchase::RepEngagement#retailer_origin?),
the invoice's own party — the marketplace seller account the outlet name
comes off — and the project's rep.Deliberately no
quotesoractivities; #for_review explains why
preloading those is dead weight. { customer: %i[original_source source], invoice: { customer: :catalog }, opportunity: %i[customer primary_sales_rep] }.freeze
Instance Attribute Summary collapse
- #relation ⇒ ActiveRecord::Relation readonly
Instance Method Summary collapse
-
#accepted ⇒ CustomerOutletPurchaseQuery
Accepted — verified by the orders team, or added by a rep.
-
#awaiting_verification ⇒ CustomerOutletPurchaseQuery
Swept proposals the orders team has not looked at yet.
-
#created_since(since) ⇒ CustomerOutletPurchaseQuery
Everything the nightly sweep proposed in a window, newest first — what the daily digest reports on.
-
#customer_level_only ⇒ CustomerOutletPurchaseQuery
Purchases the customer made with no project attached — no product line in common with any quote, or no live opportunity at all.
-
#for_display ⇒ ActiveRecord::Relation
Loaded for display: every association the digest and the CRM tables read.
-
#for_rep(employee_id) ⇒ CustomerOutletPurchaseQuery
Rep on the linked opportunity, for per-rep digests and queues.
-
#for_review ⇒ ActiveRecord::Relation
Loaded for the review queue.
-
#from_nightly_sweep ⇒ CustomerOutletPurchaseQuery
Proposed by the sweep rather than linked by a human.
-
#initialize(relation = CustomerOutletPurchase.all) ⇒ CustomerOutletPurchaseQuery
constructor
A new instance of CustomerOutletPurchaseQuery.
- #through_outlets(catalog_ids) ⇒ CustomerOutletPurchaseQuery
-
#total_revenue ⇒ BigDecimal
Outlet revenue across the current relation.
Constructor Details
#initialize(relation = CustomerOutletPurchase.all) ⇒ CustomerOutletPurchaseQuery
Returns a new instance of CustomerOutletPurchaseQuery.
24 25 26 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 24 def initialize(relation = CustomerOutletPurchase.all) @relation = relation end |
Instance Attribute Details
#relation ⇒ ActiveRecord::Relation (readonly)
29 30 31 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 29 def relation @relation end |
Instance Method Details
#accepted ⇒ CustomerOutletPurchaseQuery
Accepted — verified by the orders team, or added by a rep.
57 58 59 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 57 def accepted chain(relation.accepted) end |
#awaiting_verification ⇒ CustomerOutletPurchaseQuery
Swept proposals the orders team has not looked at yet.
50 51 52 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 50 def awaiting_verification chain(relation.unverified) end |
#created_since(since) ⇒ CustomerOutletPurchaseQuery
Everything the nightly sweep proposed in a window, newest first — what the
daily digest reports on.
36 37 38 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 36 def created_since(since) chain(relation.where(created_at: since..)) end |
#customer_level_only ⇒ CustomerOutletPurchaseQuery
Purchases the customer made with no project attached — no product line in
common with any quote, or no live opportunity at all.
65 66 67 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 65 def customer_level_only chain(relation.customer_level) end |
#for_display ⇒ ActiveRecord::Relation
Loaded for display: every association the digest and the CRM tables read.
The digest reports rep effort beside confidence, so it reads the buyer's
source rows per row the same way the queue does — hence the shared
REVIEW_ASSOCIATIONS.
93 94 95 96 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 93 def for_display relation.includes(:verified_by, REVIEW_ASSOCIATIONS) .order(created_at: :desc) end |
#for_rep(employee_id) ⇒ CustomerOutletPurchaseQuery
Rep on the linked opportunity, for per-rep digests and queues.
82 83 84 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 82 def for_rep(employee_id) chain(relation.where(opportunity: Opportunity.assigned_to_rep(employee_id))) end |
#for_review ⇒ ActiveRecord::Relation
Loaded for the review queue.
Preloading quotes and activities looks right and does nothing:
CustomerOutletPurchase::RepEngagement reaches them through
activities.count and quotes.order(:created_at).first, and both .count
and .order build a NEW relation that queries whatever is already loaded —
so eager-loading them buys rows nobody reads. Its other two signals come off
Communication and CallRecord at class level, which no includes can
reach. What DOES pay is the purchase's own customer and that customer's two
source rows: every row reads them for the name and for
RepEngagement#retailer_origin?.
NULLS LAST because confidence is nullable and Postgres sorts NULL FIRST
on DESC — an unscored row would otherwise head a queue ordered by strength.
Strongest match first. Deliberately NOT ordered by engagement: that score is
computed per row in Ruby, so ordering on it would mean loading the whole
queue before rendering any of it, and it is the disagreement between the two
numbers that the reviewer needs to see — not either ranking alone.
119 120 121 122 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 119 def for_review relation.includes(REVIEW_ASSOCIATIONS) .order(Arel.sql('confidence DESC NULLS LAST'), created_at: :desc) end |
#from_nightly_sweep ⇒ CustomerOutletPurchaseQuery
Proposed by the sweep rather than linked by a human.
43 44 45 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 43 def from_nightly_sweep chain(relation.from_nightly) end |
#through_outlets(catalog_ids) ⇒ CustomerOutletPurchaseQuery
71 72 73 74 75 76 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 71 def through_outlets(catalog_ids) ids = Array(catalog_ids).compact_blank return self if ids.empty? chain(relation.joins(invoice: :customer).where(parties: { catalog_id: ids })) end |
#total_revenue ⇒ BigDecimal
Outlet revenue across the current relation.
Summed over DISTINCT invoices, not rows: one invoice fans out to every
opportunity it plausibly matches (a Turner Electric invoice hit five at the
same address), and joining would count its total once per link.
131 132 133 |
# File 'app/queries/customer_outlet_purchase_query.rb', line 131 def total_revenue Invoice.where(id: relation.select(:invoice_id)).sum(:total) end |