Add getClosingTimeForDate that queries default_hours_scheduled_changes for a pending staged change whose effective_date <= the booking date. Falls back to current working_hours if no staged change applies. Also checks the staged schedule to see if a weekday is closed (returns 00:00).
86 lines
2.8 KiB
Go
86 lines
2.8 KiB
Go
package bookings
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"crussell/db"
|
|
)
|
|
|
|
// ErrPastClosing is returned by checkClosingHours when the booking's end time
|
|
// exceeds the working hours closing time for that day.
|
|
var ErrPastClosing = errors.New("booking extends beyond closing hours")
|
|
|
|
// checkClosingHours verifies that a booking end time (in Europe/London wall-clock
|
|
// time) does not exceed the closing time stored as "HH:MM" in the working_hours
|
|
// table. Returns ErrPastClosing if the booking runs past close, or a generic
|
|
// error if closeStr cannot be parsed.
|
|
// getClosingTimeForDate resolves the closing time for a booking on a given
|
|
// date/weekday. It first checks for a pending default_hours_scheduled_changes
|
|
// whose effective_date <= the booking date; if found, the staged hours' closing
|
|
// time for that weekday is returned. Otherwise falls back to the current
|
|
// working_hours table. Returns the closing time as "HH:MM" (or "HH:MM:SS").
|
|
func getClosingTimeForDate(ctx context.Context, q db.Querier, weekday int, bookingDate time.Time) (string, error) {
|
|
// Check for a pending staged change that applies to this date
|
|
type stagedHour struct {
|
|
Weekday int `json:"weekday"`
|
|
StartTime string `json:"startTime"`
|
|
EndTime string `json:"endTime"`
|
|
IsOpen bool `json:"isOpen"`
|
|
}
|
|
var hoursJSON string
|
|
err := q.QueryRow(ctx, `
|
|
SELECT hours::text
|
|
FROM default_hours_scheduled_changes
|
|
WHERE effective_date <= $1::date
|
|
AND applied_at IS NULL
|
|
AND cancelled_at IS NULL
|
|
ORDER BY effective_date DESC
|
|
LIMIT 1
|
|
`, bookingDate.Format("2006-01-02")).Scan(&hoursJSON)
|
|
if err == nil && hoursJSON != "" {
|
|
var staged []stagedHour
|
|
if json.Unmarshal([]byte(hoursJSON), &staged) == nil {
|
|
for _, h := range staged {
|
|
if h.Weekday == weekday && h.IsOpen {
|
|
return h.EndTime, nil
|
|
}
|
|
}
|
|
}
|
|
// Staged change found but weekday is closed — return "00:00" so the
|
|
// caller can reject the booking (the day would be closed under the
|
|
// staged schedule).
|
|
return "00:00", nil
|
|
}
|
|
|
|
// Fall back to current working_hours table
|
|
var closeStr string
|
|
if err := q.QueryRow(ctx, `SELECT end_time::text FROM working_hours WHERE weekday = $1`, weekday).Scan(&closeStr); err != nil {
|
|
return "", err
|
|
}
|
|
return closeStr, nil
|
|
}
|
|
|
|
func checkClosingHours(localEnd time.Time, closeStr string) error {
|
|
parts := strings.Split(closeStr, ":")
|
|
if len(parts) < 2 {
|
|
return errors.New("invalid closing time format")
|
|
}
|
|
closeHour, err := strconv.Atoi(parts[0])
|
|
if err != nil {
|
|
return errors.New("invalid closing time format")
|
|
}
|
|
closeMin, err := strconv.Atoi(parts[1])
|
|
if err != nil {
|
|
return errors.New("invalid closing time format")
|
|
}
|
|
if localEnd.Hour() > closeHour || (localEnd.Hour() == closeHour && localEnd.Minute() > closeMin) {
|
|
return ErrPastClosing
|
|
}
|
|
return nil
|
|
}
|