Files
project-work/worker-toolkit-stocks-in-the-future/repo/docs/map/objects/money/earnings-summary.md

3.1 KiB

type, cluster, universe, status, entity
type cluster universe status entity
object money live verified app/models/earnings_summary.rb

EarningsSummary

A plain Ruby object (not an Active Record model) that totals a portfolio's earnings by reason for the "where did my money come from" panel.

Verified 2026-08-16 against commit 63732df.

Why this shape

It lives in app/models/ but has no table and no superclass (app/models/earnings_summary.rb:3). It wraps a portfolio and runs one grouped sum per reason (:36-41) — five queries per render, deliberately simple rather than a single grouped query, because it is only ever built for one student at a time.

Known defect — transaction_fees_cents always returns 0. sum_by_reason filters .deposits, i.e. transaction_type: :deposit (:38), but fee rows are written with transaction_type: :fee by TransactionFeeProcessor (app/services/transaction_fee_processor.rb:26-29). The two never intersect, so transaction_fees_cents (:30-32) sums an empty set. It is rendered to students as "Transaction Fees" at app/views/portfolios/_earnings_summary_card.html.erb:22, where it always shows $0.00. The fix is to drop .deposits for that one reason — but note that total_earnings_cents (:26-28) deliberately excludes fees, so changing sum_by_reason wholesale would alter the total too.

Shape

  • PORO; initialize(portfolio) (:6-8)
  • Readers, all in cents: attendance_earnings_cents, reading_earnings_cents, math_earnings_cents, awards_cents, total_earnings_cents, transaction_fees_cents (:10-32)
  • total_earnings_cents = attendance + reading + math + awards (:26-28). Fees are not subtracted.
  • No caching, no memoization — each reader hits the database

It covers four of the seven reason values. administrative_adjustments and transaction_fees are not part of the total; grade_earnings is leftover.

Connected to

  • owns: —
  • owned-by: portfolio (by construction, not by association)
  • joins: reads portfolio-transaction
  • looks-like-but-is-not: not an Active Record model — EarningsSummary.find and any scope or callback do not exist. It also is not the balance: it counts income only and ignores debits, credits, and withdrawals entirely.

If you change this

  • Hits: PortfoliosController#show (app/controllers/portfolios_controller.rb:10) and Admin::StudentsController#show (app/controllers/admin/students_controller.rb:21); the two views that render it — app/views/portfolios/_earnings_summary_card.html.erb and app/views/admin/students/show.html.erb:85-100.
  • Does not hit: portfolio#cash_balance. This class is read-only and entirely parallel to the balance calculation — correcting the fee bug here changes a displayed figure, not anyone's spendable money.

Surfaces

Surface Role
PortfoliosController#show student/teacher read
Admin::StudentsController#show admin read

See

  • Source: app/models/earnings_summary.rb