package services import ( "context" "fmt" "time" "github.com/breakpilot/school-service/internal/models" ) // RolloverSchoolYear advances every tt_class for this user by one grade // level, removes graduating classes (grade > 13), and updates the // cal_school_config school-year dates. Operates as a single transaction. // // Stammdaten (teachers, subjects, rooms, periods) bleiben unveraendert — // es aendern sich nur Klassen-Stufen. func (s *CalendarService) RolloverSchoolYear(ctx context.Context, userID string, req *models.SchoolYearRolloverRequest) (*models.SchoolYearRolloverResult, error) { newStart, newEnd := defaultSchoolYearDates(req) tx, err := s.db.Begin(ctx) if err != nil { return nil, err } defer tx.Rollback(ctx) // 1. Remove the graduating cohort first so they don't get bumped to 14. gradRes, err := tx.Exec(ctx, ` DELETE FROM tt_class WHERE created_by_user_id = $1 AND grade_level >= 13 `, userID) if err != nil { return nil, fmt.Errorf("delete graduating: %w", err) } // 2. Promote everyone else. promRes, err := tx.Exec(ctx, ` UPDATE tt_class SET grade_level = grade_level + 1 WHERE created_by_user_id = $1 `, userID) if err != nil { return nil, fmt.Errorf("promote classes: %w", err) } // 3. Update the school-year dates in the config (creates a row if the // user never picked a Bundesland — but that's an edge case; in normal // flow the wizard has run before rollover). _, err = tx.Exec(ctx, ` UPDATE cal_school_config SET school_year_start = $1::date, school_year_end = $2::date, updated_at = NOW() WHERE user_id = $3 `, newStart, newEnd, userID) if err != nil { return nil, fmt.Errorf("update config: %w", err) } if err := tx.Commit(ctx); err != nil { return nil, err } return &models.SchoolYearRolloverResult{ ClassesPromoted: int(promRes.RowsAffected()), ClassesGraduated: int(gradRes.RowsAffected()), NewYearStart: newStart, NewYearEnd: newEnd, }, nil } // defaultSchoolYearDates returns the dates from the request if both set, // otherwise the next school year starting Aug 1 of "this year or next" // and ending Jul 31 the year after. func defaultSchoolYearDates(req *models.SchoolYearRolloverRequest) (string, string) { if req != nil && req.NewYearStart != nil && req.NewYearEnd != nil { return *req.NewYearStart, *req.NewYearEnd } now := time.Now() startYear := now.Year() // If we're past August, the "new" year refers to the next calendar year. if int(now.Month()) >= 8 { startYear++ } start := time.Date(startYear, 8, 1, 0, 0, 0, 0, time.UTC) end := time.Date(startYear+1, 7, 31, 0, 0, 0, 0, time.UTC) return start.Format("2006-01-02"), end.Format("2006-01-02") }