init push
This commit is contained in:
128
internal/domain/helicopter/model.go
Normal file
128
internal/domain/helicopter/model.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package helicopter
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
filemanager "wucher/internal/domain/file_manager"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
const (
|
||||
StatusAvailable = "available"
|
||||
StatusBooked = "booked"
|
||||
StatusAOG = "aog"
|
||||
StatusMCF = "mcf"
|
||||
)
|
||||
|
||||
const (
|
||||
AOGSourceUnknown = ""
|
||||
AOGSourceManual = "manual"
|
||||
AOGSourceAuto = "auto"
|
||||
AOGSourceComplaint = "complaint"
|
||||
)
|
||||
|
||||
// StatusRank is the display order for fleet and hold/defect listings — most critical
|
||||
// first: MCF, then AOG, then Booked, then Available (any unknown status sorts last).
|
||||
func StatusRank(status string) int {
|
||||
switch status {
|
||||
case StatusMCF:
|
||||
return 0
|
||||
case StatusAOG:
|
||||
return 1
|
||||
case StatusBooked:
|
||||
return 2
|
||||
case StatusAvailable:
|
||||
return 3
|
||||
default:
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
func IsValidStatus(s string) bool {
|
||||
switch s {
|
||||
case StatusAvailable, StatusBooked, StatusAOG, StatusMCF:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type StatusInput struct {
|
||||
IsBook bool
|
||||
IsMCF bool
|
||||
ComplaintGrounded bool
|
||||
}
|
||||
|
||||
func (h *Helicopter) DeriveStatus(in StatusInput) string {
|
||||
switch {
|
||||
case in.IsMCF:
|
||||
return StatusMCF
|
||||
case h.AirOnGround || in.ComplaintGrounded:
|
||||
return StatusAOG
|
||||
case in.IsBook:
|
||||
return StatusBooked
|
||||
default:
|
||||
return StatusAvailable
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Helicopter) EnforceAOGInactive() {
|
||||
if h.AirOnGround {
|
||||
h.IsActive = false
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Helicopter) EnforceMCFInactive(mcf bool) {
|
||||
if mcf {
|
||||
h.IsActive = false
|
||||
}
|
||||
}
|
||||
|
||||
type SortKeyPatch struct {
|
||||
Set bool
|
||||
Value *int
|
||||
}
|
||||
|
||||
type Helicopter struct {
|
||||
ID []byte `gorm:"type:binary(16);primaryKey;column:id"`
|
||||
Designation string `gorm:"type:varchar(100);not null;column:designation"`
|
||||
Identifier string `gorm:"type:varchar(100);uniqueIndex;not null;column:identifier"`
|
||||
ReportSequence int `gorm:"not null;default:0;column:report_sequence"`
|
||||
Type string `gorm:"type:varchar(100);not null;column:type"`
|
||||
SortKey *int `gorm:"column:sortkey"`
|
||||
Engine1 string `gorm:"type:varchar(100);column:engine_1"`
|
||||
Engine2 string `gorm:"type:varchar(100);column:engine_2"`
|
||||
Consumption float64 `gorm:"type:decimal(10,2);not null;default:0;column:consumption_lt_min"`
|
||||
NR1 bool `gorm:"type:tinyint(1);not null;default:0;column:nr1"`
|
||||
NR2 bool `gorm:"type:tinyint(1);not null;default:0;column:nr2"`
|
||||
LH bool `gorm:"type:tinyint(1);not null;default:0;column:lh"`
|
||||
RH bool `gorm:"type:tinyint(1);not null;default:0;column:rh"`
|
||||
MGB bool `gorm:"type:tinyint(1);not null;default:0;column:mgb"`
|
||||
IGB bool `gorm:"type:tinyint(1);not null;default:0;column:igb"`
|
||||
TGB bool `gorm:"type:tinyint(1);not null;default:0;column:tgb"`
|
||||
UTCOffset int `gorm:"not null;default:0;column:utc_offset"`
|
||||
FotoAttachmentID []byte `gorm:"type:binary(16);index;column:foto_attachment_id"`
|
||||
Dry bool `gorm:"type:tinyint(1);not null;default:0;column:dry"`
|
||||
AirOnGround bool `gorm:"type:tinyint(1);not null;default:0;column:air_on_ground"`
|
||||
AOGReason string `gorm:"type:text;column:aog_reason"`
|
||||
AOGSource string `gorm:"type:varchar(16);not null;default:'';column:aog_source"`
|
||||
Note string `gorm:"type:text;column:note"`
|
||||
IsActive bool `gorm:"type:tinyint(1);index;not null;default:1;column:is_active"`
|
||||
CreatedBy []byte `gorm:"type:binary(16);column:created_by"`
|
||||
UpdatedBy []byte `gorm:"type:binary(16);column:updated_by"`
|
||||
DeletedAt *time.Time `gorm:"column:deleted_at"`
|
||||
CreatedByName string `gorm:"->;column:created_by_name"`
|
||||
UpdatedByName string `gorm:"->;column:updated_by_name"`
|
||||
FotoAttachment *filemanager.Attachment `gorm:"foreignKey:FotoAttachmentID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (Helicopter) TableName() string { return "helicopters" }
|
||||
|
||||
func (h *Helicopter) BeforeCreate(tx *gorm.DB) error {
|
||||
if len(h.ID) == 0 {
|
||||
h.ID = uuidv7.MustBytes()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
53
internal/domain/helicopter/model_test.go
Normal file
53
internal/domain/helicopter/model_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package helicopter
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHelicopterEnforceAOGInactive(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
airOnGround bool
|
||||
initActive bool
|
||||
want bool
|
||||
}{
|
||||
{name: "aog forces inactive", airOnGround: true, initActive: true, want: false},
|
||||
{name: "aog stays inactive", airOnGround: true, initActive: false, want: false},
|
||||
{name: "not aog keeps manual active", airOnGround: false, initActive: true, want: true},
|
||||
{name: "not aog keeps manual inactive", airOnGround: false, initActive: false, want: false},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := Helicopter{AirOnGround: tc.airOnGround, IsActive: tc.initActive}
|
||||
h.EnforceAOGInactive()
|
||||
if h.IsActive != tc.want {
|
||||
t.Fatalf("IsActive = %v, want %v", h.IsActive, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelicopterDeriveStatus(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
airOnGround bool
|
||||
mcf bool
|
||||
isInUse bool
|
||||
want string
|
||||
}{
|
||||
{name: "available when idle", airOnGround: false, mcf: false, isInUse: false, want: StatusAvailable},
|
||||
{name: "booked when in use", airOnGround: false, mcf: false, isInUse: true, want: StatusBooked},
|
||||
{name: "mcf when mcf flagged", airOnGround: false, mcf: true, isInUse: false, want: StatusMCF},
|
||||
{name: "mcf outranks booked", airOnGround: false, mcf: true, isInUse: true, want: StatusMCF},
|
||||
{name: "aog when on ground", airOnGround: true, mcf: false, isInUse: false, want: StatusAOG},
|
||||
{name: "aog outranks booked", airOnGround: true, mcf: false, isInUse: true, want: StatusAOG},
|
||||
{name: "mcf outranks aog", airOnGround: true, mcf: true, isInUse: false, want: StatusMCF},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := Helicopter{AirOnGround: tc.airOnGround}
|
||||
if got := h.DeriveStatus(StatusInput{IsBook: tc.isInUse, IsMCF: tc.mcf}); got != tc.want {
|
||||
t.Fatalf("DeriveStatus() = %q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
16
internal/domain/helicopter/repository.go
Normal file
16
internal/domain/helicopter/repository.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package helicopter
|
||||
|
||||
import "context"
|
||||
|
||||
type Repository interface {
|
||||
Create(ctx context.Context, helicopter *Helicopter) error
|
||||
Update(ctx context.Context, helicopter *Helicopter) error
|
||||
ExistsByIdentifier(ctx context.Context, identifier string, excludeID []byte) (bool, error)
|
||||
Delete(ctx context.Context, id []byte) error
|
||||
GetByID(ctx context.Context, id []byte) (*Helicopter, error)
|
||||
List(ctx context.Context, filter string, statuses []string, sort string, limit, offset int, groundedIDs [][]byte) ([]Helicopter, int64, error)
|
||||
NextReportNumber(ctx context.Context, id []byte) (string, int, error)
|
||||
IsInUse(ctx context.Context, id []byte) (bool, error)
|
||||
InUseByAircraftIDs(ctx context.Context, ids [][]byte) (map[string]bool, error)
|
||||
ActiveFlightIDByAircraftIDs(ctx context.Context, ids [][]byte) (map[string][]byte, error)
|
||||
}
|
||||
17
internal/domain/helicopter/service.go
Normal file
17
internal/domain/helicopter/service.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package helicopter
|
||||
|
||||
import "context"
|
||||
|
||||
type Service interface {
|
||||
Create(ctx context.Context, helicopter *Helicopter) error
|
||||
Update(ctx context.Context, helicopter *Helicopter) error
|
||||
ExistsByIdentifier(ctx context.Context, identifier string, excludeID []byte) (bool, error)
|
||||
ApplySortKeyPatch(helicopter *Helicopter, patch SortKeyPatch)
|
||||
Delete(ctx context.Context, id []byte) error
|
||||
GetByID(ctx context.Context, id []byte) (*Helicopter, error)
|
||||
List(ctx context.Context, filter string, statuses []string, sort string, limit, offset int, groundedIDs [][]byte) ([]Helicopter, int64, error)
|
||||
NextReportNumber(ctx context.Context, id []byte) (string, int, error)
|
||||
IsInUse(ctx context.Context, id []byte) (bool, error)
|
||||
InUseByAircraftIDs(ctx context.Context, ids [][]byte) (map[string]bool, error)
|
||||
ActiveFlightIDByAircraftIDs(ctx context.Context, ids [][]byte) (map[string][]byte, error)
|
||||
}
|
||||
Reference in New Issue
Block a user