feat: check staged default hours in all booking creation paths
Integrate getClosingTimeForDate into AdminReserveSlotHandler, CreateBookingHandler, AdminRescheduleBookingHandler, and AdminCreateBookingForUserHandler. Rejects bookings when the staged schedule marks a day as closed (00:00).
This commit is contained in:
@@ -119,16 +119,18 @@ func AdminReserveSlotHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
localStart := req.StartTime.In(londonLocation)
|
localStart := req.StartTime.In(londonLocation)
|
||||||
// DB uses 0=Monday..6=Sunday; Go uses 0=Sunday..6=Saturday. Convert.
|
// DB uses 0=Monday..6=Sunday; Go uses 0=Sunday..6=Saturday. Convert.
|
||||||
weekday := int((localStart.Weekday() + 6) % 7)
|
weekday := int((localStart.Weekday() + 6) % 7)
|
||||||
var closeStr string
|
|
||||||
if err := db.Conn.QueryRow(r.Context(), `SELECT end_time FROM working_hours WHERE weekday = $1`, weekday).Scan(&closeStr); err != nil {
|
closeStr, err := getClosingTimeForDate(r.Context(), db.Conn, weekday, localStart)
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if err != nil {
|
||||||
http.Error(w, "Not open on this day", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("Failed to get hours: %v", err)
|
log.Printf("Failed to get hours: %v", err)
|
||||||
http.Error(w, "Could not verify hours", http.StatusInternalServerError)
|
http.Error(w, "Could not verify hours", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 00:00 means the day is closed under the staged schedule — reject outright
|
||||||
|
if closeStr == "00:00" || closeStr == "00:00:00" {
|
||||||
|
http.Error(w, "Not open on this day", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
localEndLondon := localStart.Add(time.Duration(svcDuration) * time.Minute).In(londonLocation)
|
localEndLondon := localStart.Add(time.Duration(svcDuration) * time.Minute).In(londonLocation)
|
||||||
if err := checkClosingHours(localEndLondon, closeStr); err != nil {
|
if err := checkClosingHours(localEndLondon, closeStr); err != nil {
|
||||||
|
|||||||
@@ -2197,12 +2197,17 @@ func CreateBookingHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
localStart := req.StartTime.In(londonLocation)
|
localStart := req.StartTime.In(londonLocation)
|
||||||
// DB uses 0=Monday..6=Sunday; Go uses 0=Sunday..6=Saturday. Convert.
|
// DB uses 0=Monday..6=Sunday; Go uses 0=Sunday..6=Saturday. Convert.
|
||||||
weekday := int((localStart.Weekday() + 6) % 7)
|
weekday := int((localStart.Weekday() + 6) % 7)
|
||||||
var closeStr string
|
|
||||||
if err := db.Conn.QueryRow(r.Context(), `SELECT end_time FROM working_hours WHERE weekday = $1`, weekday).Scan(&closeStr); err != nil {
|
closeStr, err := getClosingTimeForDate(r.Context(), db.Conn, weekday, localStart)
|
||||||
|
if err != nil {
|
||||||
log.Printf("Failed to get hours: %v", err)
|
log.Printf("Failed to get hours: %v", err)
|
||||||
http.Error(w, "Could not verify hours", http.StatusInternalServerError)
|
http.Error(w, "Could not verify hours", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if closeStr == "00:00" || closeStr == "00:00:00" {
|
||||||
|
http.Error(w, "Not open on this day", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
endTime := req.StartTime.Add(time.Duration(svcDuration) * time.Minute)
|
endTime := req.StartTime.Add(time.Duration(svcDuration) * time.Minute)
|
||||||
localEndLondon := localStart.Add(time.Duration(svcDuration) * time.Minute).In(londonLocation)
|
localEndLondon := localStart.Add(time.Duration(svcDuration) * time.Minute).In(londonLocation)
|
||||||
@@ -4287,6 +4292,11 @@ func AdminRescheduleBookingHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Cannot reschedule to a closed day", http.StatusBadRequest)
|
http.Error(w, "Cannot reschedule to a closed day", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Also check the day isn't closed under a staged default hours change
|
||||||
|
if closeStr, err := getClosingTimeForDate(r.Context(), tx, weekday, localStart); err == nil && (closeStr == "00:00" || closeStr == "00:00:00") {
|
||||||
|
http.Error(w, "This day will be closed under the upcoming schedule change", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
if _, evictErr := EvictPendingReleaseOverlapping(r.Context(), tx, req.StartTime, newEndTime); evictErr != nil {
|
if _, evictErr := EvictPendingReleaseOverlapping(r.Context(), tx, req.StartTime, newEndTime); evictErr != nil {
|
||||||
log.Printf("Failed to evict pending_release bookings on reschedule: %v", evictErr)
|
log.Printf("Failed to evict pending_release bookings on reschedule: %v", evictErr)
|
||||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||||
|
|||||||
@@ -643,6 +643,14 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Cannot book during holiday hours when the salon is closed", http.StatusConflict)
|
http.Error(w, "Cannot book during holiday hours when the salon is closed", http.StatusConflict)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also check if the day is closed under a staged default hours change
|
||||||
|
// whose effective_date has been reached.
|
||||||
|
closeStr, err := getClosingTimeForDate(r.Context(), db.Conn, weekday, localStart)
|
||||||
|
if err == nil && (closeStr == "00:00" || closeStr == "00:00:00") {
|
||||||
|
http.Error(w, "Not open on this day under upcoming schedule change", http.StatusConflict)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for overlapping confirmed/in_progress/completed bookings
|
// Check for overlapping confirmed/in_progress/completed bookings
|
||||||
|
|||||||
Reference in New Issue
Block a user