package dutyroster import ( "time" "gorm.io/gorm" "wucher/internal/domain/auth" "wucher/internal/domain/base" "wucher/internal/domain/takeover" "wucher/internal/shared/pkg/uuidv7" ) // DutyRoster stores roster header per base and duty date. type DutyRoster struct { ID []byte `gorm:"type:binary(16);primaryKey;column:id"` FlightID []byte `gorm:"type:binary(16);index:idx_roster_flight_id,unique;column:flight_id"` BaseID []byte `gorm:"type:binary(16);not null;index:idx_roster_base_date;column:base_id"` DutyDate time.Time `gorm:"type:date;not null;index:idx_roster_base_date;column:duty_date"` CreatedAt time.Time `gorm:"column:created_at"` CreatedBy []byte `gorm:"type:binary(16);column:created_by"` UpdatedAt time.Time `gorm:"column:updated_at"` UpdatedBy []byte `gorm:"type:binary(16);column:updated_by"` DeletedAt *time.Time `gorm:"column:deleted_at"` DeletedBy []byte `gorm:"type:binary(16);column:deleted_by"` Base *base.Base `gorm:"foreignKey:BaseID;references:ID;-:migration"` Crews []DutyRosterCrew `gorm:"foreignKey:RosterID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` } func (DutyRoster) TableName() string { return "duty_rosters" } func (h *DutyRoster) BeforeCreate(tx *gorm.DB) error { if len(h.ID) == 0 { h.ID = uuidv7.MustBytes() } return nil } type DutyRosterCrew struct { ID []byte `gorm:"type:binary(16);primaryKey;column:id"` RosterID []byte `gorm:"type:binary(16);not null;index:idx_roster_crew_role;index:idx_roster_crew_unique,unique;column:roster_id"` UserID []byte `gorm:"type:binary(16);index:idx_roster_crew_unique,unique;column:user_id"` TakeoverAcID []byte `gorm:"type:binary(16);index;column:takeover_ac_id"` RoleCode string `gorm:"type:varchar(64);not null;index:idx_roster_crew_role;index:idx_roster_crew_unique,unique;check:chk_roster_crew_role_code,role_code IN ('pilot','doctor','rescuer','helper','other_person','air_rescuer','flight_assistant');column:role_code"` CrewType string `gorm:"type:varchar(32);not null;default:main;index:idx_roster_crew_unique,unique;check:chk_roster_crew_type,crew_type IN ('main','additional');column:crew_type"` NameLabel string `gorm:"type:varchar(191);index:idx_roster_crew_unique,unique;column:name_label"` MobilePhone string `gorm:"type:varchar(64);column:mobile_phone"` Email string `gorm:"type:varchar(191);column:email"` ConfirmAt *time.Time `gorm:"column:confirm_at"` DateStart *time.Time `gorm:"type:date;index:idx_roster_crew_unique,unique;check:chk_roster_crew_date_range,date_start IS NULL OR date_end IS NULL OR date_start <= date_end;column:date_start"` DateEnd *time.Time `gorm:"type:date;index:idx_roster_crew_unique,unique;column:date_end"` ShiftStart string `gorm:"type:time;index:idx_roster_crew_unique,unique;column:shift_start"` ShiftEnd string `gorm:"type:time;index:idx_roster_crew_unique,unique;column:shift_end"` FlightInstructor bool `gorm:"type:tinyint(1);not null;default:0;column:flight_instructor"` LineChecker bool `gorm:"type:tinyint(1);not null;default:0;column:line_checker"` Supervisor bool `gorm:"type:tinyint(1);not null;default:0;column:supervisor"` Examiner bool `gorm:"type:tinyint(1);not null;default:0;column:examiner"` CoPilot bool `gorm:"type:tinyint(1);not null;default:0;column:co_pilot"` Roster *DutyRoster `gorm:"foreignKey:RosterID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` User *auth.User `gorm:"foreignKey:UserID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"` TakeoverAc *takeover.TakeoverAc `gorm:"foreignKey:TakeoverAcID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"` CreatedAt time.Time `gorm:"column:created_at"` CreatedBy []byte `gorm:"type:binary(16);column:created_by"` UpdatedAt time.Time `gorm:"column:updated_at"` UpdatedBy []byte `gorm:"type:binary(16);column:updated_by"` DeletedAt *time.Time `gorm:"column:deleted_at"` DeletedBy []byte `gorm:"type:binary(16);column:deleted_by"` } func (DutyRosterCrew) TableName() string { return "duty_roster_crews" } func (h *DutyRosterCrew) BeforeCreate(tx *gorm.DB) error { if len(h.ID) == 0 { h.ID = uuidv7.MustBytes() } return nil } // HeaderView is a denormalized header row for list/get responses. type HeaderView struct { ID []byte BaseID []byte BaseName string BaseAbbreviation string DefaultShiftStart string DefaultShiftEnd string HelicopterID []byte HelicopterType string HelicopterDesignation string HelicopterIdentifier string DutyDate time.Time ShiftStart string ShiftEnd string CreatedAt time.Time CreatedBy []byte UpdatedAt time.Time } // BaseView is a lightweight base projection used in roster listing. type BaseView struct { ID []byte BaseName string BaseAbbreviation string } // CrewView is a denormalized crew row for list/get responses. type CrewView struct { ID []byte RosterID []byte UserID []byte Name string Role string RoleCode string CrewType string MobilePhone string Email string DateStart *time.Time DateEnd *time.Time ShiftStart string ShiftEnd string FlightInstructor bool LineChecker bool Supervisor bool Examiner bool CoPilot bool } type DeletedCrewResult struct { ID []byte RosterID []byte UserID []byte Name string RoleCode string CrewType string DateStart *time.Time DateEnd *time.Time }