convert pdf to md, add OVERVIEW and docs
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
type: object
|
||||
cluster: gradebook
|
||||
universe: live
|
||||
status: verified
|
||||
entity: app/models/grade_book.rb
|
||||
---
|
||||
|
||||
# GradeBook
|
||||
|
||||
One [classroom](../org/classroom.md)'s grades for one [quarter](../org/quarter.md), 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](../org/classroom.md) 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
|
||||
- `status` is 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](grade-entry.md)
|
||||
- **owned-by:** [classroom](../org/classroom.md), [quarter](../org/quarter.md)
|
||||
- **joins:** —
|
||||
- **looks-like-but-is-not:** `verified` is not a human review state; see Why.
|
||||
And a `GradeBook` is not a [grade-level](../org/grade-level.md).
|
||||
|
||||
## If you change this
|
||||
|
||||
- **Hits:** [portfolio-transaction](../money/portfolio-transaction.md) — finalizing mints
|
||||
deposits; the [finalize-gradebook-earnings](../../processes/finalize-gradebook-earnings.md)
|
||||
movement; [grade-entry](grade-entry.md) via `dependent: :destroy`;
|
||||
`GradeBookPolicy`; the autosave Stimulus controller, which PATCHes entries into the
|
||||
`update` action.
|
||||
- **Does not hit:** [order](../trading/order.md) or any holding. Earnings arrive as cash
|
||||
deposits only — finalizing never buys, sells, or touches
|
||||
[portfolio-stock](../trading/portfolio-stock.md).
|
||||
|
||||
## 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`
|
||||
Reference in New Issue
Block a user