394 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			394 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable File
		
	
	
package application
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"slices"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/sorting"
 | 
						|
	filestorage "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
 | 
						|
	fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi"
 | 
						|
	"git.coopgo.io/coopgo-platform/fleets/storage"
 | 
						|
	groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi"
 | 
						|
	groupsmanagementstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
 | 
						|
	mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi"
 | 
						|
	mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
 | 
						|
	"github.com/google/uuid"
 | 
						|
	"github.com/paulmach/orb/geojson"
 | 
						|
	"github.com/rs/zerolog/log"
 | 
						|
	"google.golang.org/protobuf/types/known/structpb"
 | 
						|
	"google.golang.org/protobuf/types/known/timestamppb"
 | 
						|
)
 | 
						|
 | 
						|
type VehiclesSearchResult struct {
 | 
						|
	Vehicles              []storage.Vehicle
 | 
						|
	Beneficiary           mobilityaccountsstorage.Account
 | 
						|
	BeneficiaryDocuments  []filestorage.FileInfo
 | 
						|
	Groups                map[string]any
 | 
						|
	Searched              bool
 | 
						|
	StartDate             string
 | 
						|
	EndDate               string
 | 
						|
	VehicleType           string
 | 
						|
	Automatic             bool
 | 
						|
	MandatoryDocuments    []string
 | 
						|
	FileTypesMap          map[string]string
 | 
						|
	VehicleTypes          []string
 | 
						|
	Beneficiaries         []mobilityaccountsstorage.Account
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) SearchVehicles(ctx context.Context, beneficiaryID, startDateStr, endDateStr, vehicleType string, automatic bool) (*VehiclesSearchResult, error) {
 | 
						|
	var beneficiary mobilityaccountsstorage.Account
 | 
						|
	beneficiarydocuments := []filestorage.FileInfo{}
 | 
						|
	vehicles := []storage.Vehicle{}
 | 
						|
	searched := false
 | 
						|
	administrators := []string{}
 | 
						|
 | 
						|
	startdate, err := time.Parse("2006-01-02", startDateStr)
 | 
						|
	if err != nil {
 | 
						|
		startdate = time.Time{}
 | 
						|
	}
 | 
						|
	enddate, err := time.Parse("2006-01-02", endDateStr)
 | 
						|
	if err != nil {
 | 
						|
		enddate = time.Time{}
 | 
						|
	}
 | 
						|
 | 
						|
	if beneficiaryID != "" && startdate.After(time.Now().Add(-24*time.Hour)) && enddate.After(startdate) {
 | 
						|
		// Handler form
 | 
						|
		searched = true
 | 
						|
 | 
						|
		requestbeneficiary := &mobilityaccounts.GetAccountRequest{
 | 
						|
			Id: beneficiaryID,
 | 
						|
		}
 | 
						|
 | 
						|
		respbeneficiary, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, requestbeneficiary)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to get beneficiary: %w", err)
 | 
						|
		}
 | 
						|
 | 
						|
		beneficiary = respbeneficiary.Account.ToStorageType()
 | 
						|
 | 
						|
		request := &fleets.GetVehiclesRequest{
 | 
						|
			Namespaces:       []string{"parcoursmob"},
 | 
						|
			AvailabilityFrom: timestamppb.New(startdate),
 | 
						|
			AvailabilityTo:   timestamppb.New(enddate),
 | 
						|
		}
 | 
						|
 | 
						|
		if vehicleType != "" {
 | 
						|
			request.Types = []string{vehicleType}
 | 
						|
		}
 | 
						|
 | 
						|
		resp, err := h.services.GRPC.Fleets.GetVehicles(ctx, request)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to get vehicles: %w", err)
 | 
						|
		}
 | 
						|
 | 
						|
		for _, vehicle := range resp.Vehicles {
 | 
						|
			v := vehicle.ToStorageType()
 | 
						|
 | 
						|
			if vehicleType == "Voiture" && automatic {
 | 
						|
				if auto, ok := v.Data["automatic"].(bool); !ok || !auto {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			adminfound := false
 | 
						|
			for _, a := range administrators {
 | 
						|
				if a == v.Administrators[0] {
 | 
						|
					adminfound = true
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if !adminfound {
 | 
						|
				administrators = append(administrators, v.Administrators[0])
 | 
						|
			}
 | 
						|
 | 
						|
			vehicles = append(vehicles, v)
 | 
						|
		}
 | 
						|
 | 
						|
		// Sort vehicles if beneficiary address is set
 | 
						|
		if beneficiaryAddress, ok := beneficiary.Data["address"]; ok {
 | 
						|
			beneficiaryAddressJson, err := json.Marshal(beneficiaryAddress)
 | 
						|
			if err == nil {
 | 
						|
				beneficiaryAddressGeojson, err := geojson.UnmarshalFeature(beneficiaryAddressJson)
 | 
						|
				if err == nil {
 | 
						|
					slices.SortFunc(vehicles, sorting.VehiclesByDistanceFrom(*beneficiaryAddressGeojson))
 | 
						|
				} else {
 | 
						|
					log.Error().Err(err).Msg("error transforming beneficiary address to GeoJSON")
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				log.Error().Err(err).Msg("error transforming beneficiary address to JSON")
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		beneficiarydocuments = h.filestorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + beneficiary.ID)
 | 
						|
	}
 | 
						|
 | 
						|
	accounts, err := h.services.GetBeneficiariesMap()
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to get beneficiaries: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Convert map to slice for compatibility
 | 
						|
	beneficiaries := make([]mobilityaccountsstorage.Account, 0, len(accounts))
 | 
						|
	for _, account := range accounts {
 | 
						|
		beneficiaries = append(beneficiaries, account)
 | 
						|
	}
 | 
						|
 | 
						|
	groups := map[string]any{}
 | 
						|
	if len(administrators) > 0 {
 | 
						|
		admingroups, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(ctx, &groupsmanagement.GetGroupsBatchRequest{
 | 
						|
			Groupids: administrators,
 | 
						|
		})
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to get admin groups: %w", err)
 | 
						|
		}
 | 
						|
 | 
						|
		for _, g := range admingroups.Groups {
 | 
						|
			groups[g.Id] = g.ToStorageType()
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	sort.Sort(sorting.BeneficiariesByName(beneficiaries))
 | 
						|
 | 
						|
	mandatoryDocuments := h.config.GetStringSlice("modules.fleets.booking_documents.mandatory")
 | 
						|
	fileTypesMap := h.config.GetStringMapString("storage.files.file_types")
 | 
						|
	vehicleTypes := h.config.GetStringSlice("modules.fleets.vehicle_types")
 | 
						|
 | 
						|
	return &VehiclesSearchResult{
 | 
						|
		Vehicles:             vehicles,
 | 
						|
		Beneficiary:          beneficiary,
 | 
						|
		BeneficiaryDocuments: beneficiarydocuments,
 | 
						|
		Groups:               groups,
 | 
						|
		Searched:             searched,
 | 
						|
		StartDate:            startDateStr,
 | 
						|
		EndDate:              endDateStr,
 | 
						|
		VehicleType:          vehicleType,
 | 
						|
		Automatic:            automatic,
 | 
						|
		MandatoryDocuments:   mandatoryDocuments,
 | 
						|
		FileTypesMap:         fileTypesMap,
 | 
						|
		VehicleTypes:         vehicleTypes,
 | 
						|
		Beneficiaries:        beneficiaries,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
type BookVehicleResult struct {
 | 
						|
	BookingID string
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) BookVehicle(ctx context.Context, vehicleID, beneficiaryID, startDateStr, endDateStr string, documents map[string]io.Reader, documentHeaders map[string]string, existingDocs map[string]string, currentUserID string, currentUserClaims map[string]any, currentGroup any) (*BookVehicleResult, error) {
 | 
						|
	group := currentGroup.(groupsmanagementstorage.Group)
 | 
						|
 | 
						|
	vehicle, err := h.services.GRPC.Fleets.GetVehicle(ctx, &fleets.GetVehicleRequest{
 | 
						|
		Vehicleid: vehicleID,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("vehicle not found: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	startdate, err := time.Parse("2006-01-02", startDateStr)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("invalid start date: %w", err)
 | 
						|
	}
 | 
						|
	enddate, err := time.Parse("2006-01-02", endDateStr)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("invalid end date: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	data := map[string]any{
 | 
						|
		"booked_by": map[string]any{
 | 
						|
			"user": map[string]any{
 | 
						|
				"id":           currentUserID,
 | 
						|
				"display_name": fmt.Sprintf("%s %s", currentUserClaims["first_name"], currentUserClaims["last_name"]),
 | 
						|
			},
 | 
						|
			"group": map[string]any{
 | 
						|
				"id":   group.ID,
 | 
						|
				"name": group.Data["name"],
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	datapb, err := structpb.NewStruct(data)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to create booking metadata: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	bookingID := uuid.NewString()
 | 
						|
	booking := &fleets.Booking{
 | 
						|
		Id:              bookingID,
 | 
						|
		Vehicleid:       vehicleID,
 | 
						|
		Driver:          beneficiaryID,
 | 
						|
		Startdate:       timestamppb.New(startdate),
 | 
						|
		Enddate:         timestamppb.New(enddate),
 | 
						|
		Unavailablefrom: timestamppb.New(startdate),
 | 
						|
		Unavailableto:   timestamppb.New(enddate.Add(72 * time.Hour)),
 | 
						|
		Data:            datapb,
 | 
						|
	}
 | 
						|
 | 
						|
	request := &fleets.CreateBookingRequest{
 | 
						|
		Booking: booking,
 | 
						|
	}
 | 
						|
 | 
						|
	// Handle document uploads
 | 
						|
	for docType, file := range documents {
 | 
						|
		fileid := uuid.NewString()
 | 
						|
		filename := documentHeaders[docType]
 | 
						|
 | 
						|
		metadata := map[string]string{
 | 
						|
			"type": docType,
 | 
						|
			"name": filename,
 | 
						|
		}
 | 
						|
 | 
						|
		if err := h.filestorage.Put(file, filestorage.PREFIX_BOOKINGS, fmt.Sprintf("%s/%s_%s", bookingID, fileid, filename), -1, metadata); err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to upload document %s: %w", docType, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Handle existing documents
 | 
						|
	for docType, existingFile := range existingDocs {
 | 
						|
		path := strings.Split(existingFile, "/")
 | 
						|
		if err := h.filestorage.Copy(existingFile, fmt.Sprintf("%s/%s/%s", filestorage.PREFIX_BOOKINGS, bookingID, path[len(path)-1])); err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to copy existing document %s: %w", docType, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = h.services.GRPC.Fleets.CreateBooking(ctx, request)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to create booking: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	// NOTIFY GROUP MEMBERS
 | 
						|
	members, _, err := h.groupmembers(vehicle.Vehicle.Administrators[0])
 | 
						|
	if err != nil {
 | 
						|
		log.Error().Err(err).Msg("failed to get group members for notification")
 | 
						|
	} else {
 | 
						|
		for _, m := range members {
 | 
						|
			if email, ok := m.Data["email"].(string); ok {
 | 
						|
				h.emailing.Send("fleets.bookings.creation_admin_alert", email, map[string]string{
 | 
						|
					"bookingid": bookingID,
 | 
						|
				})
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return &BookVehicleResult{
 | 
						|
		BookingID: bookingID,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
type VehicleBookingDetailsResult struct {
 | 
						|
	Booking      storage.Booking
 | 
						|
	Vehicle      storage.Vehicle
 | 
						|
	Beneficiary  mobilityaccountsstorage.Account
 | 
						|
	Group        groupsmanagementstorage.Group
 | 
						|
	Documents    []filestorage.FileInfo
 | 
						|
	FileTypesMap map[string]string
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) GetVehicleBookingDetails(ctx context.Context, bookingID string) (*VehicleBookingDetailsResult, error) {
 | 
						|
	request := &fleets.GetBookingRequest{
 | 
						|
		Bookingid: bookingID,
 | 
						|
	}
 | 
						|
	resp, err := h.services.GRPC.Fleets.GetBooking(ctx, request)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to get booking: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	booking := resp.Booking.ToStorageType()
 | 
						|
 | 
						|
	beneficiaryrequest := &mobilityaccounts.GetAccountRequest{
 | 
						|
		Id: booking.Driver,
 | 
						|
	}
 | 
						|
 | 
						|
	beneficiaryresp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, beneficiaryrequest)
 | 
						|
	if err != nil {
 | 
						|
		beneficiaryresp = &mobilityaccounts.GetAccountResponse{
 | 
						|
			Account: &mobilityaccounts.Account{},
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	grouprequest := &groupsmanagement.GetGroupRequest{
 | 
						|
		Id: booking.Vehicle.Administrators[0],
 | 
						|
	}
 | 
						|
 | 
						|
	groupresp, err := h.services.GRPC.GroupsManagement.GetGroup(ctx, grouprequest)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to get group: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	documents := h.filestorage.List(filestorage.PREFIX_BOOKINGS + "/" + bookingID)
 | 
						|
	fileTypesMap := h.config.GetStringMapString("storage.files.file_types")
 | 
						|
 | 
						|
	return &VehicleBookingDetailsResult{
 | 
						|
		Booking:      booking,
 | 
						|
		Vehicle:      booking.Vehicle,
 | 
						|
		Beneficiary:  beneficiaryresp.Account.ToStorageType(),
 | 
						|
		Group:        groupresp.Group.ToStorageType(),
 | 
						|
		Documents:    documents,
 | 
						|
		FileTypesMap: fileTypesMap,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
type VehicleBookingsListResult struct {
 | 
						|
	Bookings    []storage.Booking
 | 
						|
	VehiclesMap map[string]storage.Vehicle
 | 
						|
	GroupsMap   map[string]groupsmanagementstorage.Group
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) GetVehicleBookingsList(ctx context.Context, groupID string) (*VehicleBookingsListResult, error) {
 | 
						|
	request := &fleets.GetBookingsRequest{}
 | 
						|
	resp, err := h.services.GRPC.Fleets.GetBookings(ctx, request)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to get bookings: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	bookings := []storage.Booking{}
 | 
						|
 | 
						|
	for _, b := range resp.Bookings {
 | 
						|
		booking := b.ToStorageType()
 | 
						|
		if b1, ok := booking.Data["booked_by"].(map[string]any); ok {
 | 
						|
			if b2, ok := b1["group"].(map[string]any); ok {
 | 
						|
				if b2["id"] == groupID {
 | 
						|
					bookings = append(bookings, booking)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	vehiclesMap, err := h.services.GetVehiclesMap()
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to get vehicles map: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	groupsMap, err := h.services.GetGroupsMap()
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to get groups map: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return &VehicleBookingsListResult{
 | 
						|
		Bookings:    bookings,
 | 
						|
		VehiclesMap: vehiclesMap,
 | 
						|
		GroupsMap:   groupsMap,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) GetBookingDocument(ctx context.Context, bookingID, document string) (io.Reader, string, error) {
 | 
						|
	file, info, err := h.filestorage.Get(filestorage.PREFIX_BOOKINGS, fmt.Sprintf("%s/%s", bookingID, document))
 | 
						|
	if err != nil {
 | 
						|
		return nil, "", fmt.Errorf("failed to get document: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return file, info.ContentType, nil
 | 
						|
}
 | 
						|
 | 
						|
// Helper method to expose config to web handlers
 | 
						|
func (h *ApplicationHandler) GetConfig() interface{} {
 | 
						|
	return h.config
 | 
						|
}
 |