154 lines
4.8 KiB
Go
154 lines
4.8 KiB
Go
package complaint
|
||
|
||
import (
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
|
||
flightdomain "wucher/internal/domain/flight"
|
||
helicopter "wucher/internal/domain/helicopter"
|
||
"wucher/internal/shared/pkg/uuidv7"
|
||
)
|
||
|
||
const (
|
||
MELSeverityNonMEL int8 = 0
|
||
MELSeverityA int8 = 1
|
||
MELSeverityB int8 = 2
|
||
MELSeverityC int8 = 3
|
||
MELSeverityD int8 = 4
|
||
)
|
||
|
||
const (
|
||
StatusPendingMEL = "pending_mel"
|
||
StatusActiveNonMEL = "active_non_mel"
|
||
StatusActiveMEL = "active_mel"
|
||
StatusServiced = "serviced"
|
||
)
|
||
|
||
var melGraceDays = map[int8]int{
|
||
MELSeverityA: 1,
|
||
MELSeverityB: 3,
|
||
MELSeverityC: 10,
|
||
MELSeverityD: 120,
|
||
}
|
||
|
||
type ListFilter struct {
|
||
Search string
|
||
DateFrom string
|
||
DateTo string
|
||
Person string
|
||
HelicopterID []byte
|
||
FlightID []byte
|
||
HoldItemsOnly bool
|
||
}
|
||
|
||
func MELGraceDays(severity int8) int { return melGraceDays[severity] }
|
||
|
||
type Complaint struct {
|
||
ID []byte `gorm:"type:binary(16);primaryKey;column:id"`
|
||
HelicopterID []byte `gorm:"type:binary(16);not null;index;column:helicopter_id"`
|
||
FlightID []byte `gorm:"type:binary(16);index;column:flight_id"`
|
||
|
||
Description string `gorm:"type:text;not null;column:description"`
|
||
ReportedBy []byte `gorm:"type:binary(16);not null;column:reported_by"`
|
||
ReportedAt time.Time `gorm:"type:datetime(3);not null;column:reported_at"`
|
||
AircraftHoursAtReport *string `gorm:"type:varchar(20);column:aircraft_hours_at_report"`
|
||
|
||
MELSeverity int8 `gorm:"type:tinyint;not null;default:0;column:mel_severity"`
|
||
MELClassifiedAt *time.Time `gorm:"type:datetime(3);column:mel_classified_at"`
|
||
MELClassifiedBy []byte `gorm:"type:binary(16);column:mel_classified_by"`
|
||
|
||
IsNSR bool `gorm:"type:boolean;not null;default:false;column:is_nsr"`
|
||
NSRDecidedAt *time.Time `gorm:"type:datetime(3);column:nsr_decided_at"`
|
||
NSRDecidedBy []byte `gorm:"type:binary(16);column:nsr_decided_by"`
|
||
NSRReason *string `gorm:"type:text;column:nsr_reason"`
|
||
|
||
ActionTaken *string `gorm:"type:text;column:action_taken"`
|
||
AircraftHoursAtFix *string `gorm:"type:varchar(20);column:aircraft_hours_at_fix"`
|
||
|
||
FixedAt *time.Time `gorm:"type:datetime(3);column:fixed_at"`
|
||
FixedByEASAID []byte `gorm:"type:binary(16);column:fixed_by_easa_id"`
|
||
|
||
Helicopter *helicopter.Helicopter `gorm:"foreignKey:HelicopterID;references:ID"`
|
||
Flight *flightdomain.Flight `gorm:"foreignKey:FlightID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`
|
||
|
||
CreatedAt time.Time `gorm:"type:datetime(3);column:created_at"`
|
||
CreatedBy []byte `gorm:"type:binary(16);column:created_by"`
|
||
UpdatedAt time.Time `gorm:"type:datetime(3);column:updated_at"`
|
||
UpdatedBy []byte `gorm:"type:binary(16);column:updated_by"`
|
||
DeletedAt *time.Time `gorm:"type:datetime(3);index;column:deleted_at"`
|
||
DeletedBy []byte `gorm:"type:binary(16);column:deleted_by"`
|
||
}
|
||
|
||
func (Complaint) TableName() string { return "complaints" }
|
||
|
||
func (c *Complaint) BeforeCreate(_ *gorm.DB) error {
|
||
if len(c.ID) == 0 {
|
||
c.ID = uuidv7.MustBytes()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c *Complaint) Status() string {
|
||
if c == nil {
|
||
return StatusActiveNonMEL
|
||
}
|
||
if c.FixedAt != nil {
|
||
return StatusServiced
|
||
}
|
||
// mel_severity is optional at creation; until it is classified (mel_classified_at set)
|
||
// the complaint is "pending_mel" — its severity value (0) is a default, not "Non-MEL".
|
||
if c.MELClassifiedAt == nil {
|
||
return StatusPendingMEL
|
||
}
|
||
if c.MELSeverity > 0 {
|
||
return StatusActiveMEL
|
||
}
|
||
return StatusActiveNonMEL
|
||
}
|
||
|
||
func (c *Complaint) MELDeadline() *time.Time {
|
||
if c == nil || c.MELSeverity <= 0 {
|
||
return nil
|
||
}
|
||
grace := MELGraceDays(c.MELSeverity)
|
||
if grace <= 0 || c.ReportedAt.IsZero() {
|
||
return nil
|
||
}
|
||
// MEL grace is measured in days: A=1, B=3, C=10, D=120 days from report.
|
||
deadline := c.ReportedAt.Add(time.Duration(grace) * 24 * time.Hour)
|
||
return &deadline
|
||
}
|
||
|
||
func (c *Complaint) IsMELAOG(now time.Time) bool {
|
||
if c == nil || c.IsNSR {
|
||
return false
|
||
}
|
||
deadline := c.MELDeadline()
|
||
if deadline == nil {
|
||
return false
|
||
}
|
||
return !now.Before(*deadline)
|
||
}
|
||
|
||
// IsGrounding reports whether this complaint currently grounds the aircraft (AOG).
|
||
// Returning to service requires a formal release: only a Non-Safety Release (NSR /
|
||
// MEL deferral) or a signed EASA release (CRS, sets FixedAt) lifts the grounding.
|
||
// Recording action_taken alone does NOT lift it — corrective work is not a release.
|
||
//
|
||
// A pending (not-yet-classified) complaint does NOT ground: an unassessed report is not
|
||
// yet a grounding defect. Grounding starts at classification — NON-MEL grounds
|
||
// immediately, MEL A–D grounds once its grace deadline lapses.
|
||
func (c *Complaint) IsGrounding(now time.Time) bool {
|
||
if c == nil || c.IsNSR || c.FixedAt != nil {
|
||
return false
|
||
}
|
||
if c.MELClassifiedAt == nil {
|
||
return false
|
||
}
|
||
if c.MELSeverity <= 0 {
|
||
return true
|
||
}
|
||
return c.IsMELAOG(now)
|
||
}
|