556 lines
22 KiB
Go
556 lines
22 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
beforeflightinspection "wucher/internal/domain/before_flight_inspection"
|
|
"wucher/internal/domain/flight"
|
|
flightinspection "wucher/internal/domain/flight_inspection"
|
|
flightinspectionfilechecklist "wucher/internal/domain/flight_inspection_file_checklist"
|
|
flightprepcheck "wucher/internal/domain/flight_prep_check"
|
|
helicopterfile "wucher/internal/domain/helicopter_file"
|
|
reserveac "wucher/internal/domain/reserve_ac"
|
|
"wucher/internal/shared/pkg/uuidv7"
|
|
requestdto "wucher/internal/transport/http/dto/request"
|
|
)
|
|
|
|
// ReserveAcUpdateError carries structured validation/update failures from the
|
|
// application service layer back to the HTTP handler.
|
|
type ReserveAcUpdateError struct {
|
|
Status int
|
|
Title string
|
|
Detail string
|
|
Pointer string
|
|
}
|
|
|
|
func (e *ReserveAcUpdateError) Error() string {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
return e.Detail
|
|
}
|
|
|
|
type ReserveAcUpdateInput struct {
|
|
ID []byte
|
|
ActorUserID []byte
|
|
AircraftID *string
|
|
FlightID *string
|
|
Inspection *requestdto.ReserveAcInspectionUpdateAttributes
|
|
Flight reserveAcFlightService
|
|
Inspect flightinspection.FlightInspectionService
|
|
Before beforeflightinspection.Service
|
|
Prep flightprepcheck.Service
|
|
HeliFile helicopterfile.Service
|
|
FileChecklist flightinspectionfilechecklist.Service
|
|
}
|
|
|
|
type reserveAcFlightService interface {
|
|
GetByID(ctx context.Context, id []byte) (*flight.Flight, error)
|
|
GetByReserveAcID(ctx context.Context, reserveAcID []byte) (*flight.Flight, error)
|
|
Update(ctx context.Context, row *flight.Flight) error
|
|
}
|
|
|
|
type ReserveAcCreateInput struct {
|
|
HelicopterID []byte
|
|
FlightID []byte
|
|
InspectionDate time.Time
|
|
Before *requestdto.ReserveAcBeforeInspectionAttributes
|
|
Prepare *requestdto.ReserveAcPrepareInspectionAttributes
|
|
ActorUserID []byte
|
|
Flight reserveAcFlightService
|
|
Inspect flightinspection.FlightInspectionService
|
|
BeforeSvc beforeflightinspection.Service
|
|
Prep flightprepcheck.Service
|
|
HeliFile helicopterfile.Service
|
|
FileChecklist flightinspectionfilechecklist.Service
|
|
}
|
|
|
|
func (s *ReserveAcService) UpdateDetailed(ctx context.Context, in ReserveAcUpdateInput) (*reserveac.ReserveAc, error) {
|
|
if s == nil || s.repo == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Update failed", Detail: "reserve ac service is not configured"}
|
|
}
|
|
if len(in.ID) != 16 {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "id is invalid UUID", Pointer: "/data/id"}
|
|
}
|
|
|
|
existing, err := s.repo.GetByID(ctx, in.ID)
|
|
if err != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: err.Error()}
|
|
}
|
|
if existing == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 404, Title: "Not found", Detail: "reserve_ac not found"}
|
|
}
|
|
|
|
if in.FlightID != nil {
|
|
if in.Flight == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Update failed", Detail: "flight service is not configured"}
|
|
}
|
|
flightValue := strings.TrimSpace(*in.FlightID)
|
|
flightID, parseErr := uuidv7.ParseString(flightValue)
|
|
if parseErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "flight_id is invalid UUID", Pointer: "/data/attributes/flight_id"}
|
|
}
|
|
|
|
targetFlight, flightErr := in.Flight.GetByID(ctx, flightID)
|
|
if flightErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: flightErr.Error()}
|
|
}
|
|
if targetFlight == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "flight_id does not exist", Pointer: "/data/attributes/flight_id"}
|
|
}
|
|
if len(targetFlight.ReserveAcID) == 16 && !bytes.Equal(targetFlight.ReserveAcID, existing.ID) {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "flight already has reserve_ac assigned", Pointer: "/data/attributes/flight_id"}
|
|
}
|
|
|
|
currentFlight, currentErr := in.Flight.GetByReserveAcID(ctx, existing.ID)
|
|
if currentErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: currentErr.Error()}
|
|
}
|
|
if currentFlight != nil && !bytes.Equal(currentFlight.ID, targetFlight.ID) {
|
|
currentFlight.ReserveAcID = nil
|
|
currentFlight.UpdatedBy = in.ActorUserID
|
|
if updateErr := in.Flight.Update(ctx, currentFlight); updateErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: updateErr.Error()}
|
|
}
|
|
}
|
|
|
|
if len(targetFlight.ReserveAcID) != 16 || !bytes.Equal(targetFlight.ReserveAcID, existing.ID) {
|
|
targetFlight.ReserveAcID = append([]byte(nil), existing.ID...)
|
|
targetFlight.UpdatedBy = in.ActorUserID
|
|
if updateErr := in.Flight.Update(ctx, targetFlight); updateErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: updateErr.Error()}
|
|
}
|
|
}
|
|
}
|
|
|
|
if in.AircraftID != nil && strings.TrimSpace(*in.AircraftID) != "" {
|
|
helicopterValue := strings.TrimSpace(*in.AircraftID)
|
|
helicopterID, parseErr := uuidv7.ParseString(helicopterValue)
|
|
if parseErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "helicopter_id is invalid UUID", Pointer: "/data/attributes/helicopter_id"}
|
|
}
|
|
exists, existsErr := s.repo.AircraftExists(ctx, helicopterID)
|
|
if existsErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: existsErr.Error()}
|
|
}
|
|
if !exists {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "helicopter_id does not exist", Pointer: "/data/attributes/helicopter_id"}
|
|
}
|
|
existing.AircraftID = helicopterID
|
|
}
|
|
|
|
if in.Inspection != nil {
|
|
if in.Inspect == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Update failed", Detail: "flight inspection service is not configured"}
|
|
}
|
|
if in.Before == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Update failed", Detail: "before flight inspection service is not configured"}
|
|
}
|
|
if in.Prep == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Update failed", Detail: "flight prep check service is not configured"}
|
|
}
|
|
if in.HeliFile == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Update failed", Detail: "helicopter file service is not configured"}
|
|
}
|
|
if in.FileChecklist == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Update failed", Detail: "flight inspection file checklist service is not configured"}
|
|
}
|
|
|
|
inspectionRow, getErr := in.Inspect.GetByID(ctx, existing.InspectionID)
|
|
if getErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: getErr.Error()}
|
|
}
|
|
if inspectionRow == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 404, Title: "Not found", Detail: "flight inspection not found"}
|
|
}
|
|
|
|
if in.Inspection.InspectionDate != nil {
|
|
inspectionDate, parseErr := time.Parse("2006-01-02", strings.TrimSpace(*in.Inspection.InspectionDate))
|
|
if parseErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "inspection.inspection_date must be YYYY-MM-DD", Pointer: "/data/attributes/inspection/inspection_date"}
|
|
}
|
|
inspectionRow.InspectionDate = inspectionDate.UTC()
|
|
inspectionRow.UpdatedBy = in.ActorUserID
|
|
if updateErr := in.Inspect.Update(ctx, inspectionRow); updateErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: updateErr.Error()}
|
|
}
|
|
}
|
|
|
|
if in.Inspection.Before != nil {
|
|
beforeReq := &beforeflightinspection.UpsertRequest{
|
|
OilEngineNR1Checked: in.Inspection.Before.OilEngineNR1Checked,
|
|
OilEngineNR2Checked: in.Inspection.Before.OilEngineNR2Checked,
|
|
HydraulicLHChecked: in.Inspection.Before.HydraulicLHChecked,
|
|
HydraulicRHChecked: in.Inspection.Before.HydraulicRHChecked,
|
|
OilTransmissionMGBChecked: in.Inspection.Before.OilTransmissionMGBChecked,
|
|
OilTransmissionIGBChecked: in.Inspection.Before.OilTransmissionIGBChecked,
|
|
OilTransmissionTGBChecked: in.Inspection.Before.OilTransmissionTGBChecked,
|
|
FuelAmount: in.Inspection.Before.FuelAmount,
|
|
FuelUnit: in.Inspection.Before.FuelUnit,
|
|
FuelAddedAmount: in.Inspection.Before.FuelAddedAmount,
|
|
Note: in.Inspection.Before.Note,
|
|
}
|
|
if _, upsertErr := in.Before.Upsert(ctx, existing.InspectionID, beforeReq); upsertErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: upsertErr.Error()}
|
|
}
|
|
if in.Inspection.Before.FileChecklist != nil {
|
|
if err := s.upsertInspectionFileChecklistSection(ctx, in, existing.InspectionID, existing.AircraftID, helicopterfile.SectionBeforeFirstFlightInspection, in.Inspection.Before.FileChecklist, "/data/attributes/inspection/before/file_checklist", existing.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
if in.Inspection.Prepare != nil {
|
|
if _, upsertErr := in.Prep.UpsertByFlightInspection(ctx, existing.InspectionID, &flightprepcheck.UpsertRequest{
|
|
NOTAMBriefing: in.Inspection.Prepare.NotamBriefing,
|
|
WeatherBriefing: in.Inspection.Prepare.WeatherBriefing,
|
|
OperationalFlightPlan: in.Inspection.Prepare.OperationalFlightPlan,
|
|
}); upsertErr != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: upsertErr.Error()}
|
|
}
|
|
if in.Inspection.Prepare.FileChecklist != nil {
|
|
if err := s.upsertInspectionFileChecklistSection(ctx, in, existing.InspectionID, existing.AircraftID, helicopterfile.SectionFlightPreparationAndWB, in.Inspection.Prepare.FileChecklist, "/data/attributes/inspection/prepare/file_checklist", existing.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
existing.UpdatedBy = in.ActorUserID
|
|
existing.Status = s.calculateReserveAcStatus(ctx, in, existing.InspectionID, existing.AircraftID)
|
|
if err := s.Update(ctx, existing); err != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Update failed", Detail: err.Error()}
|
|
}
|
|
|
|
return existing, nil
|
|
}
|
|
|
|
func (s *ReserveAcService) CreateDetailed(ctx context.Context, in ReserveAcCreateInput) (*reserveac.ReserveAc, error) {
|
|
if s == nil || s.repo == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Create failed", Detail: "reserve ac service is not configured"}
|
|
}
|
|
if len(in.HelicopterID) != 16 {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "helicopter_id is invalid UUID", Pointer: "/data/attributes/helicopter_id"}
|
|
}
|
|
if len(in.FlightID) != 16 {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "flight_id is invalid UUID", Pointer: "/data/attributes/flight_id"}
|
|
}
|
|
if in.Before == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "inspection.before is required", Pointer: "/data/attributes/inspection/before"}
|
|
}
|
|
if in.Prepare == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "inspection.prepare is required", Pointer: "/data/attributes/inspection/prepare"}
|
|
}
|
|
if in.Flight == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Create failed", Detail: "flight service is not configured"}
|
|
}
|
|
if in.Inspect == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Create failed", Detail: "flight inspection service is not configured"}
|
|
}
|
|
if in.BeforeSvc == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Create failed", Detail: "before flight inspection service is not configured"}
|
|
}
|
|
if in.Prep == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Create failed", Detail: "flight prep check service is not configured"}
|
|
}
|
|
if in.HeliFile == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Create failed", Detail: "helicopter file service is not configured"}
|
|
}
|
|
if in.FileChecklist == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 500, Title: "Create failed", Detail: "flight inspection file checklist service is not configured"}
|
|
}
|
|
|
|
flightRow, err := in.Flight.GetByID(ctx, in.FlightID)
|
|
if err != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
if flightRow == nil {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "flight_id does not exist", Pointer: "/data/attributes/flight_id"}
|
|
}
|
|
if len(flightRow.ReserveAcID) == 16 {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "flight already has reserve_ac assigned", Pointer: "/data/attributes/flight_id"}
|
|
}
|
|
|
|
exists, err := s.repo.AircraftExists(ctx, in.HelicopterID)
|
|
if err != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
if !exists {
|
|
return nil, &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "helicopter_id does not exist", Pointer: "/data/attributes/helicopter_id"}
|
|
}
|
|
|
|
inspection := &flightinspection.FlightInspection{
|
|
InspectionDate: in.InspectionDate.UTC(),
|
|
CreatedBy: in.ActorUserID,
|
|
UpdatedBy: in.ActorUserID,
|
|
}
|
|
if err := in.Inspect.CreateDraft(ctx, inspection); err != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
|
|
row := &reserveac.ReserveAc{
|
|
Status: reserveac.StatusPending,
|
|
AircraftID: append([]byte(nil), in.HelicopterID...),
|
|
InspectionID: append([]byte(nil), inspection.ID...),
|
|
CreatedBy: in.ActorUserID,
|
|
UpdatedBy: in.ActorUserID,
|
|
}
|
|
if err := s.Create(ctx, row); err != nil {
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
|
|
beforeReq := &beforeflightinspection.UpsertRequest{
|
|
OilEngineNR1Checked: in.Before.OilEngineNR1Checked,
|
|
OilEngineNR2Checked: in.Before.OilEngineNR2Checked,
|
|
HydraulicLHChecked: in.Before.HydraulicLHChecked,
|
|
HydraulicRHChecked: in.Before.HydraulicRHChecked,
|
|
OilTransmissionMGBChecked: in.Before.OilTransmissionMGBChecked,
|
|
OilTransmissionIGBChecked: in.Before.OilTransmissionIGBChecked,
|
|
OilTransmissionTGBChecked: in.Before.OilTransmissionTGBChecked,
|
|
FuelAmount: in.Before.FuelAmount,
|
|
FuelUnit: in.Before.FuelUnit,
|
|
FuelAddedAmount: in.Before.FuelAddedAmount,
|
|
Note: in.Before.Note,
|
|
}
|
|
if _, err := in.BeforeSvc.Upsert(ctx, inspection.ID, beforeReq); err != nil {
|
|
_ = s.Delete(ctx, row.ID, in.ActorUserID)
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
if _, err := in.Prep.InitializeChecks(ctx, inspection.ID); err != nil {
|
|
_ = s.Delete(ctx, row.ID, in.ActorUserID)
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
if _, err := in.Prep.UpsertByFlightInspection(ctx, inspection.ID, &flightprepcheck.UpsertRequest{
|
|
NOTAMBriefing: in.Prepare.NotamBriefing,
|
|
WeatherBriefing: in.Prepare.WeatherBriefing,
|
|
OperationalFlightPlan: in.Prepare.OperationalFlightPlan,
|
|
}); err != nil {
|
|
_ = s.Delete(ctx, row.ID, in.ActorUserID)
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
if in.Before.FileChecklist != nil {
|
|
if err := s.upsertInspectionFileChecklistSection(ctx, ReserveAcUpdateInput{
|
|
ActorUserID: in.ActorUserID,
|
|
HeliFile: in.HeliFile,
|
|
FileChecklist: in.FileChecklist,
|
|
}, inspection.ID, in.HelicopterID, helicopterfile.SectionBeforeFirstFlightInspection, in.Before.FileChecklist, "/data/attributes/inspection/before/file_checklist", row.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if in.Prepare.FileChecklist != nil {
|
|
if err := s.upsertInspectionFileChecklistSection(ctx, ReserveAcUpdateInput{
|
|
ActorUserID: in.ActorUserID,
|
|
HeliFile: in.HeliFile,
|
|
FileChecklist: in.FileChecklist,
|
|
}, inspection.ID, in.HelicopterID, helicopterfile.SectionFlightPreparationAndWB, in.Prepare.FileChecklist, "/data/attributes/inspection/prepare/file_checklist", row.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
flightRow.ReserveAcID = append([]byte(nil), row.ID...)
|
|
flightRow.UpdatedBy = in.ActorUserID
|
|
if err := in.Flight.Update(ctx, flightRow); err != nil {
|
|
_ = s.Delete(ctx, row.ID, in.ActorUserID)
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
|
|
row.Status = s.calculateReserveAcStatus(ctx, ReserveAcUpdateInput{
|
|
Inspect: in.Inspect,
|
|
Before: in.BeforeSvc,
|
|
Prep: in.Prep,
|
|
HeliFile: in.HeliFile,
|
|
FileChecklist: in.FileChecklist,
|
|
}, inspection.ID, in.HelicopterID)
|
|
if err := s.Update(ctx, row); err != nil {
|
|
_ = s.Delete(ctx, row.ID, in.ActorUserID)
|
|
return nil, &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
|
|
return row, nil
|
|
}
|
|
|
|
func (s *ReserveAcService) calculateReserveAcStatus(ctx context.Context, in ReserveAcUpdateInput, inspectionID []byte, helicopterID []byte) string {
|
|
if len(inspectionID) != 16 || len(helicopterID) != 16 {
|
|
return reserveac.StatusPending
|
|
}
|
|
if in.Inspect == nil || in.Before == nil || in.Prep == nil || in.HeliFile == nil || in.FileChecklist == nil {
|
|
return reserveac.StatusPending
|
|
}
|
|
|
|
inspectionRow, err := in.Inspect.GetByID(ctx, inspectionID)
|
|
if err != nil || inspectionRow == nil {
|
|
return reserveac.StatusPending
|
|
}
|
|
ckRows, err := in.FileChecklist.ListByInspectionID(ctx, inspectionID)
|
|
if err != nil {
|
|
return reserveac.StatusPending
|
|
}
|
|
isDoneByHeliFileID := make(map[string]bool, len(ckRows))
|
|
for i := range ckRows {
|
|
isDoneByHeliFileID[string(ckRows[i].HelicopterFileID)] = ckRows[i].IsDone
|
|
}
|
|
|
|
beforeRows, err := in.HeliFile.ListByHelicopterAndSection(ctx, helicopterID, helicopterfile.SectionBeforeFirstFlightInspection)
|
|
if err != nil {
|
|
return reserveac.StatusPending
|
|
}
|
|
prepareRows, err := in.HeliFile.ListByHelicopterAndSection(ctx, helicopterID, helicopterfile.SectionFlightPreparationAndWB)
|
|
if err != nil {
|
|
return reserveac.StatusPending
|
|
}
|
|
beforeData, _ := in.Before.GetByFlightInspectionID(ctx, inspectionID)
|
|
prepareData, _ := in.Prep.GetByFlightInspection(ctx, inspectionID)
|
|
|
|
beforeDone := countCompletedChecklist(beforeRows, isDoneByHeliFileID)
|
|
prepareDone := countCompletedChecklist(prepareRows, isDoneByHeliFileID)
|
|
totalChecklist := len(beforeRows) + len(prepareRows)
|
|
doneChecklist := beforeDone + prepareDone
|
|
if totalChecklist > 0 {
|
|
if doneChecklist == 0 {
|
|
return reserveac.StatusPending
|
|
}
|
|
if doneChecklist < totalChecklist {
|
|
return reserveac.StatusOnProgress
|
|
}
|
|
return reserveac.StatusApproved
|
|
}
|
|
if beforeData != nil || prepareData != nil {
|
|
return reserveac.StatusOnProgress
|
|
}
|
|
return reserveac.StatusPending
|
|
}
|
|
|
|
func countCompletedChecklist(files []helicopterfile.HelicopterFile, doneByID map[string]bool) int {
|
|
if len(files) == 0 {
|
|
return 0
|
|
}
|
|
completed := 0
|
|
for i := range files {
|
|
if doneByID[string(files[i].ID)] {
|
|
completed++
|
|
}
|
|
}
|
|
return completed
|
|
}
|
|
|
|
func (s *ReserveAcService) upsertInspectionFileChecklistSection(
|
|
ctx context.Context,
|
|
in ReserveAcUpdateInput,
|
|
inspectionID []byte,
|
|
helicopterID []byte,
|
|
section string,
|
|
payload []requestdto.ReserveAcFileChecklistItem,
|
|
payloadPointer string,
|
|
reserveAcID []byte,
|
|
) error {
|
|
templateFiles, err := in.HeliFile.ListByHelicopterAndSection(ctx, helicopterID, section)
|
|
if err != nil {
|
|
if len(reserveAcID) == 16 {
|
|
_ = s.Delete(ctx, reserveAcID, in.ActorUserID)
|
|
}
|
|
return &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
|
|
type requestedChecklist struct {
|
|
fileID []byte
|
|
isDone bool
|
|
}
|
|
requested := make([]requestedChecklist, 0, len(payload))
|
|
for i := range payload {
|
|
fileID, parseErr := uuidv7.ParseString(strings.TrimSpace(payload[i].HelicopterFileID))
|
|
if parseErr != nil {
|
|
if len(reserveAcID) == 16 {
|
|
_ = s.Delete(ctx, reserveAcID, in.ActorUserID)
|
|
}
|
|
return &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "helicopter_file_id is invalid UUID", Pointer: payloadPointer}
|
|
}
|
|
requested = append(requested, requestedChecklist{
|
|
fileID: fileID,
|
|
isDone: payload[i].IsDone,
|
|
})
|
|
}
|
|
requestedByFileID := make(map[string]bool, len(requested))
|
|
for i := range requested {
|
|
requestedByFileID[string(requested[i].fileID)] = requested[i].isDone
|
|
}
|
|
|
|
existingRows, err := in.FileChecklist.ListByInspectionID(ctx, inspectionID)
|
|
if err != nil {
|
|
if len(reserveAcID) == 16 {
|
|
_ = s.Delete(ctx, reserveAcID, in.ActorUserID)
|
|
}
|
|
return &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
existingByFileID := make(map[string]*flightinspectionfilechecklist.FlightInspectionFileChecklist, len(existingRows))
|
|
for i := range existingRows {
|
|
fileID := string(existingRows[i].HelicopterFileID)
|
|
existingByFileID[fileID] = &existingRows[i]
|
|
}
|
|
|
|
templateSet := make(map[string]struct{}, len(templateFiles))
|
|
for i := range templateFiles {
|
|
fileKey := string(templateFiles[i].ID)
|
|
templateSet[fileKey] = struct{}{}
|
|
|
|
current := existingByFileID[fileKey]
|
|
targetDone, hasOverride := requestedByFileID[fileKey]
|
|
|
|
if templateFiles[i].IsMandatory && !targetDone {
|
|
if len(reserveAcID) == 16 {
|
|
_ = s.Delete(ctx, reserveAcID, in.ActorUserID)
|
|
}
|
|
return &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "file_checklist must be fully checked", Pointer: payloadPointer}
|
|
}
|
|
|
|
if current == nil {
|
|
if !hasOverride {
|
|
targetDone = false
|
|
}
|
|
newRow := &flightinspectionfilechecklist.FlightInspectionFileChecklist{
|
|
FlightInspectionID: inspectionID,
|
|
HelicopterFileID: templateFiles[i].ID,
|
|
IsDone: targetDone,
|
|
CreatedBy: in.ActorUserID,
|
|
UpdatedBy: in.ActorUserID,
|
|
}
|
|
if err := in.FileChecklist.Create(ctx, newRow); err != nil {
|
|
if len(reserveAcID) == 16 {
|
|
_ = s.Delete(ctx, reserveAcID, in.ActorUserID)
|
|
}
|
|
return &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
continue
|
|
}
|
|
|
|
if !hasOverride {
|
|
targetDone = current.IsDone
|
|
}
|
|
|
|
if current.IsDone != targetDone {
|
|
current.IsDone = targetDone
|
|
current.UpdatedBy = in.ActorUserID
|
|
if err := in.FileChecklist.Update(ctx, current); err != nil {
|
|
if len(reserveAcID) == 16 {
|
|
_ = s.Delete(ctx, reserveAcID, in.ActorUserID)
|
|
}
|
|
return &ReserveAcUpdateError{Status: 400, Title: "Create failed", Detail: err.Error()}
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := range requested {
|
|
if _, ok := templateSet[string(requested[i].fileID)]; ok {
|
|
continue
|
|
}
|
|
if len(reserveAcID) == 16 {
|
|
_ = s.Delete(ctx, reserveAcID, in.ActorUserID)
|
|
}
|
|
return &ReserveAcUpdateError{Status: 422, Title: "Validation error", Detail: "helicopter_file_id does not belong to selected helicopter/section", Pointer: payloadPointer}
|
|
}
|
|
|
|
return nil
|
|
}
|