2025-11-05 15:06:07 +00:00
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
import (
|
2025-11-05 15:07:28 +00:00
|
|
|
"errors"
|
2025-11-05 15:06:07 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Schedule represents available time slots
|
|
|
|
|
type Schedule struct {
|
|
|
|
|
gorm.Model
|
2025-11-05 15:07:28 +00:00
|
|
|
StartTime time.Time `json:"start_time" gorm:"not null;index" validate:"required"`
|
|
|
|
|
EndTime time.Time `json:"end_time" gorm:"not null;index" validate:"required"`
|
2025-11-05 15:06:07 +00:00
|
|
|
IsAvailable bool `json:"is_available" gorm:"default:true"`
|
2025-11-05 15:07:28 +00:00
|
|
|
MaxBookings int `json:"max_bookings" gorm:"default:1;check:max_bookings > 0" validate:"min=1,max=100"`
|
|
|
|
|
BookedCount int `json:"booked_count" gorm:"default:0;check:booked_count >= 0" validate:"min=0"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BeforeCreate is a GORM hook that runs before creating a schedule record
|
|
|
|
|
func (s *Schedule) BeforeCreate(tx *gorm.DB) error {
|
|
|
|
|
// Validate that end time is after start time
|
|
|
|
|
if !s.EndTime.After(s.StartTime) {
|
|
|
|
|
return errors.New("end time must be after start time")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate minimum duration (15 minutes)
|
|
|
|
|
if s.EndTime.Sub(s.StartTime) < 15*time.Minute {
|
|
|
|
|
return errors.New("schedule slot must be at least 15 minutes long")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate that start time is in the future
|
|
|
|
|
if s.StartTime.Before(time.Now()) {
|
|
|
|
|
return errors.New("schedule slot cannot be created in the past")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set default values
|
|
|
|
|
if s.MaxBookings == 0 {
|
|
|
|
|
s.MaxBookings = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BeforeUpdate is a GORM hook that runs before updating a schedule record
|
|
|
|
|
func (s *Schedule) BeforeUpdate(tx *gorm.DB) error {
|
|
|
|
|
// Validate that end time is after start time
|
|
|
|
|
if !s.EndTime.After(s.StartTime) {
|
|
|
|
|
return errors.New("end time must be after start time")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate that booked count doesn't exceed max bookings
|
|
|
|
|
if s.BookedCount > s.MaxBookings {
|
|
|
|
|
return errors.New("booked count cannot exceed max bookings")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsAvailableForBooking checks if the schedule slot has availability
|
|
|
|
|
func (s *Schedule) IsAvailableForBooking() bool {
|
|
|
|
|
return s.IsAvailable && s.BookedCount < s.MaxBookings && s.StartTime.After(time.Now())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetRemainingSlots returns the number of remaining booking slots
|
|
|
|
|
func (s *Schedule) GetRemainingSlots() int {
|
|
|
|
|
remaining := s.MaxBookings - s.BookedCount
|
|
|
|
|
if remaining < 0 {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return remaining
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Duration returns the duration of the schedule slot
|
|
|
|
|
func (s *Schedule) Duration() time.Duration {
|
|
|
|
|
return s.EndTime.Sub(s.StartTime)
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|