Answer storage
Every field value is keyed by question id, not by title:form.questionBankVersion (or the year pin on SchoolYearSettings). Titles can change; ids cannot.
Create → edit → submit
Duplicate / rollover
- Single school:
POST /api/forms/[id]/duplicatewith{ schoolYear, force? }. - District: Super Admin
POST /api/admin/forms/rolloverwith{ sourceYear, targetYear, force? }.
duplicateForm():
- Deep-clones
formData(alldataobjects). - Sets
completedwhen a step already has answers. - Creates a draft owned by the same principal.
- Sets
duplicatedFrom,schoolYear,needsUpdateflags for new/changed/revisit questions. - Does not copy comments, reviews, shares, or attestation.
- After a successful bulk rollover, the source year is archived (
SchoolYearSettings.archived = true). Archived years reject writes.
If a dashboard shows 0% on a copied plan, completions are derived from whether
formData[stepKey].data has keys (deriveCompletedSteps). New copies keep completed: true when answers exist.Compare years
GET /api/forms/[id]/compare?compareYear=2025-2026 diffs these step keys only:
attendancePlantemporaryHousingcounselingPlan
/form/[id]/compare.
Public snapshot
UnauthenticatedGET /api/public/overview aggregates school count, section count, and submitted/under_review/approved totals for currentSchoolYear().