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:
@@ -425,7 +425,14 @@ func TestRequestEditHandler_NoticePeriod_AllowsWhenEnoughNotice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.GenerateUserToken(userID)
|
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)
|
handler := http.HandlerFunc(RequestEditHandler)
|
||||||
w := makeRequest(handler, "POST", "/api/bookings/"+bookingID+"/edit-request", map[string]interface{}{
|
w := makeRequest(handler, "POST", "/api/bookings/"+bookingID+"/edit-request", map[string]interface{}{
|
||||||
|
|||||||
@@ -1310,9 +1310,6 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// M8
|
|
||||||
// L5
|
|
||||||
|
|
||||||
// Validate: at least one of new_start_time, new_services, or notes must be provided
|
// 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 {
|
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)
|
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
|
// Check booking is not already completed/cancelled
|
||||||
var currentStatus string
|
var currentStatus string
|
||||||
var currentStartTime time.Time
|
var currentStartTime time.Time
|
||||||
|
var currentDuration int
|
||||||
var depositRequired bool
|
var depositRequired bool
|
||||||
err = db.Conn.QueryRow(r.Context(), "SELECT status, start_time, deposit_required FROM bookings WHERE id = $1", bookingID).Scan(¤tStatus, ¤tStartTime, &depositRequired)
|
err = db.Conn.QueryRow(r.Context(), "SELECT status, start_time, total_duration_minutes, deposit_required FROM bookings WHERE id = $1", bookingID).Scan(¤tStatus, ¤tStartTime, ¤tDuration, &depositRequired)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get booking status %s: %v", bookingID, err)
|
log.Printf("Failed to get booking status %s: %v", bookingID, err)
|
||||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
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())
|
tx, err := db.Conn.Begin(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to start transaction: %v", err)
|
log.Printf("Failed to start transaction: %v", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user