3.1 KiB
type, cluster, universe, status, entity
| type | cluster | universe | status | entity |
|---|---|---|---|---|
| object | gradebook | live | verified | app/models/grade_book.rb |
GradeBook
One classroom's grades for one quarter, and the object whose status decides whether students get paid.
Verified 2026-08-16 against commit 63732df.
Why this shape
The model is tiny — two belongs-to, one has-many, one enum (app/models/grade_book.rb) —
but the enum is the payout gate.
status has three values: draft → verified → completed (:8-12). Read literally that
looks like a review workflow. It is not. GradeBooksController#finalize sets
verified! and calls DistributeEarnings on the very next line
(app/controllers/grade_books_controller.rb:30-31), so verified exists for a few
milliseconds. Its real job is to satisfy the service's own guard,
return unless @grade_book.verified? (app/services/distribute_earnings.rb:14), which
keeps the service safe to call from anywhere else.
Double-payment is prevented by exactly one check — the controller's
if @grade_book.completed? (app/controllers/grade_books_controller.rb:26). There is no
database constraint, no idempotency key on the resulting deposits, and
DistributeEarnings itself would happily pay twice if handed a verified book. Anything
new that finalizes a gradebook must repeat that check.
Gradebooks are never created by a controller: classroom creates one
per quarter on after_create (app/models/classroom.rb:29,112-116).
Shape
- Table
grade_books,db/schema.rb:98-107; unique on[quarter_id, classroom_id](db/schema.rb:105) — one book per classroom per quarter statusis a string column, default"draft",null: false(db/schema.rb:102)belongs_to :quarter,belongs_to :classroom(:4-5)has_many :grade_entries, dependent: :destroy(:6)
Connected to
- owns: grade-entry
- owned-by: classroom, quarter
- joins: —
- looks-like-but-is-not:
verifiedis not a human review state; see Why. And aGradeBookis not a grade-level.
If you change this
- Hits: portfolio-transaction — finalizing mints
deposits; the finalize-gradebook-earnings
movement; grade-entry via
dependent: :destroy;GradeBookPolicy; the autosave Stimulus controller, which PATCHes entries into theupdateaction. - Does not hit: order or any holding. Earnings arrive as cash deposits only — finalizing never buys, sells, or touches portfolio-stock.
Surfaces
| Surface | Role |
|---|---|
GradeBooksController (show, update, finalize) |
teacher reads/writes |
Classroom#create_gradebooks_for_quarters |
writes (creation) |
DistributeEarnings |
reads status, writes completed! |
See
- Source:
app/models/grade_book.rb,db/schema.rb:98-107 - As-built:
docs/gradebook-earnings.md