fix: add hours validation to user edit requests (was M8/L5 stubs)

RequestEditHandler had empty M8/L5 placeholder comments where hours validation was planned. Implemented full validation: staged-hours-aware closing time check via getClosingTimeForDate, closed-day check, and closing-hours boundary check using the booking's total_duration_minutes. Removed placeholder comments. Updated test to propose an open-day time that doesn't extend past closing.
This commit is contained in:
2026-08-22 00:34:49 +01:00
parent c80ad467e7
commit 991f3af540
2 changed files with 36 additions and 5 deletions
+8 -1
View File
@@ -425,7 +425,14 @@ func TestRequestEditHandler_NoticePeriod_AllowsWhenEnoughNotice(t *testing.T) {
}
token := jwt.GenerateUserToken(userID)
newTime := bookingTime.Add(48 * time.Hour)
// Set the new time to 10:00 London time on a weekday that's clearly within
// working hours. Compute from the current time to avoid DST boundary issues.
londonNow := clock.Now().In(clock.London)
nextWeekday := londonNow.AddDate(0, 0, 1)
for nextWeekday.Weekday() == time.Sunday || nextWeekday.Weekday() == time.Monday {
nextWeekday = nextWeekday.AddDate(0, 0, 1)
}
newTime := time.Date(nextWeekday.Year(), nextWeekday.Month(), nextWeekday.Day(), 10, 0, 0, 0, clock.London).UTC()
handler := http.HandlerFunc(RequestEditHandler)
w := makeRequest(handler, "POST", "/api/bookings/"+bookingID+"/edit-request", map[string]interface{}{
+28 -4
View File
@@ -1310,9 +1310,6 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
return
}
// M8
// L5
// Validate: at least one of new_start_time, new_services, or notes must be provided
if req.NewStartTime == nil && len(req.NewServices) == 0 && req.Notes == nil {
http.Error(w, "At least one of new_start_time, new_services, or notes is required", http.StatusBadRequest)
@@ -1340,8 +1337,9 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
// Check booking is not already completed/cancelled
var currentStatus string
var currentStartTime time.Time
var currentDuration int
var depositRequired bool
err = db.Conn.QueryRow(r.Context(), "SELECT status, start_time, deposit_required FROM bookings WHERE id = $1", bookingID).Scan(&currentStatus, &currentStartTime, &depositRequired)
err = db.Conn.QueryRow(r.Context(), "SELECT status, start_time, total_duration_minutes, deposit_required FROM bookings WHERE id = $1", bookingID).Scan(&currentStatus, &currentStartTime, &currentDuration, &depositRequired)
if err != nil {
log.Printf("Failed to get booking status %s: %v", bookingID, err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
@@ -1409,6 +1407,32 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
}
}
// Validate proposed time is within working hours (including staged hours changes)
if req.NewStartTime != nil {
newTime := req.NewStartTime.In(londonLocation)
weekday := int((newTime.Weekday() + 6) % 7)
// Get closing time (respects staged default hours changes)
closeStr, err := getClosingTimeForDate(r.Context(), db.Conn, weekday, newTime)
if err != nil {
log.Printf("Failed to get hours for edit request: %v", err)
http.Error(w, "Could not verify hours", http.StatusInternalServerError)
return
}
if closeStr == "00:00" || closeStr == "00:00:00" {
http.Error(w, "The salon is closed at this time on this day", http.StatusBadRequest)
return
}
// Check the proposed end time (based on the booking's service duration)
// doesn't extend beyond closing hours.
localEnd := newTime.Add(time.Duration(currentDuration) * time.Minute)
if err := checkClosingHours(localEnd, closeStr); err != nil {
http.Error(w, "This time would extend beyond closing hours", http.StatusBadRequest)
return
}
}
tx, err := db.Conn.Begin(r.Context())
if err != nil {
log.Printf("Failed to start transaction: %v", err)