package xlsx import ( "fmt" "net/http" "git.coopgo.io/coopgo-apps/parcoursmob/core/application" "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/gender" "github.com/paulmach/orb/geojson" "github.com/rs/zerolog/log" ) func (r *XLSXRenderer) SolidarityTransportBookings(w http.ResponseWriter, result *application.SolidarityTransportBookingsResult) { // Create Excel spreadsheet spreadsheet := r.NewSpreadsheet("Transport solidaire") // Build headers dynamically based on configuration headers := []string{ "ID Réservation", "Statut", "Motif de réservation", "Raison d'annulation", "Date de prise en charge", "Heure de prise en charge", } // Add driver fields from config driverOptionalFields := r.Config.Get("modules.solidarity_transport.profile_optional_fields") driverFields := []string{"last_name", "first_name", "email", "phone_number"} driverHeaders := []string{"Conducteur - Nom", "Conducteur - Prénom", "Conducteur - Email", "Conducteur - Téléphone"} if driverOptionalFieldsList, ok := driverOptionalFields.([]interface{}); ok { for _, field := range driverOptionalFieldsList { if fieldMap, ok := field.(map[string]interface{}); ok { if name, ok := fieldMap["name"].(string); ok { driverFields = append(driverFields, name) label := name if labelVal, ok := fieldMap["label"].(string); ok { label = labelVal } driverHeaders = append(driverHeaders, fmt.Sprintf("Conducteur - %s", label)) } } } } headers = append(headers, driverHeaders...) // Add beneficiary fields from config beneficiaryOptionalFields := r.Config.Get("modules.beneficiaries.profile_optional_fields") beneficiaryFields := []string{"last_name", "first_name", "email", "phone_number"} beneficiaryHeaders := []string{"Bénéficiaire - Nom", "Bénéficiaire - Prénom", "Bénéficiaire - Email", "Bénéficiaire - Téléphone"} if beneficiaryOptionalFieldsList, ok := beneficiaryOptionalFields.([]interface{}); ok { for _, field := range beneficiaryOptionalFieldsList { if fieldMap, ok := field.(map[string]interface{}); ok { if name, ok := fieldMap["name"].(string); ok { beneficiaryFields = append(beneficiaryFields, name) label := name if labelVal, ok := fieldMap["label"].(string); ok { label = labelVal } beneficiaryHeaders = append(beneficiaryHeaders, fmt.Sprintf("Bénéficiaire - %s", label)) } } } } headers = append(headers, beneficiaryHeaders...) // Add journey information headers headers = append(headers, "Lieu de départ - Adresse", "Lieu de départ - Latitude", "Lieu de départ - Longitude", "Destination - Adresse", "Destination - Latitude", "Destination - Longitude", "Distance passager (km)", "Distance conducteur totale (km)", "Durée trajet (minutes)", "Prix passager", "Devise prix passager", "Compensation conducteur", "Devise compensation", "Temps d'attente retour", "Aller simple", "Départ conducteur - Adresse", "Départ conducteur - Latitude", "Départ conducteur - Longitude", "Arrivée conducteur - Adresse", "Arrivée conducteur - Latitude", "Arrivée conducteur - Longitude", ) spreadsheet.SetHeaders(headers) // Add data rows for _, booking := range result.Bookings { driver := result.DriversMap[booking.DriverId] beneficiary := result.BeneficiariesMap[booking.PassengerId] row := []interface{}{} // Booking information row = append(row, booking.Id) row = append(row, booking.Status) // Motivation (from booking.Data) motivation := "" if booking.Data != nil { if motivationVal, ok := booking.Data["motivation"]; ok && motivationVal != nil { motivation = fmt.Sprint(motivationVal) } } row = append(row, motivation) // Cancellation reason (from booking.Data) cancellationReason := "" if booking.Data != nil { if reasonVal, ok := booking.Data["reason"]; ok && reasonVal != nil { cancellationReason = fmt.Sprint(reasonVal) } } row = append(row, cancellationReason) // Journey date and time if booking.Journey != nil { row = append(row, booking.Journey.PassengerPickupDate.Format("2006-01-02")) row = append(row, booking.Journey.PassengerPickupDate.Format("15:04")) } else { row = append(row, "", "") } // Driver data for _, field := range driverFields { row = append(row, getAccountFieldValue(driver.Data, field)) } // Beneficiary data for _, field := range beneficiaryFields { row = append(row, getAccountFieldValue(beneficiary.Data, field)) } // Journey locations and details if booking.Journey != nil { // Passenger pickup pickupAddr, pickupLat, pickupLon := getLocationData(booking.Journey.PassengerPickup) row = append(row, pickupAddr, pickupLat, pickupLon) // Passenger drop dropAddr, dropLat, dropLon := getLocationData(booking.Journey.PassengerDrop) row = append(row, dropAddr, dropLat, dropLon) // Distances and duration row = append(row, booking.Journey.PassengerDistance) row = append(row, booking.Journey.DriverDistance) row = append(row, int64(booking.Journey.Duration.Minutes())) // Pricing row = append(row, fmt.Sprintf("%.2f", booking.Journey.Price.Amount)) row = append(row, booking.Journey.Price.Currency) // Driver compensation if booking.DriverCompensationAmount > 0 { row = append(row, fmt.Sprintf("%.2f", booking.DriverCompensationAmount)) row = append(row, booking.DriverCompensationCurrency) } else { row = append(row, "", "") } // Return wait time row = append(row, "") // One way trip (Noreturn field) if booking.Journey.Noreturn { row = append(row, "Oui") } else { row = append(row, "Non") } // Driver departure driverDepartAddr, driverDepartLat, driverDepartLon := getLocationData(booking.Journey.DriverDeparture) row = append(row, driverDepartAddr, driverDepartLat, driverDepartLon) // Driver arrival driverArrivalAddr, driverArrivalLat, driverArrivalLon := getLocationData(booking.Journey.DriverArrival) row = append(row, driverArrivalAddr, driverArrivalLat, driverArrivalLon) } else { // No journey data - fill with empty values for i := 0; i < 21; i++ { row = append(row, "") } } spreadsheet.AddRow(row) } // Write Excel to response w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") w.Header().Set("Content-Disposition", "attachment; filename=\"export-transport-solidaire.xlsx\"") if err := spreadsheet.GetFile().Write(w); err != nil { log.Error().Err(err).Msg("Error generating Excel file") http.Error(w, "Error generating Excel file", http.StatusInternalServerError) return } } func getAccountFieldValue(data map[string]interface{}, field string) string { // First check direct field if val, ok := data[field]; ok && val != nil { return fmt.Sprint(val) } // Check in other_properties if otherProps, ok := data["other_properties"]; ok { if otherPropsMap, ok := otherProps.(map[string]interface{}); ok { if val, ok := otherPropsMap[field]; ok && val != nil { return fmt.Sprint(val) } } } return "" } func getLocationData(feature *geojson.Feature) (address string, lat interface{}, lon interface{}) { if feature != nil && feature.Properties != nil { if label, ok := feature.Properties["label"].(string); ok { address = label } if feature.Geometry != nil { coords := feature.Geometry.Bound().Center() lat = fmt.Sprintf("%.6f", coords.Lat()) lon = fmt.Sprintf("%.6f", coords.Lon()) } } return } func (r *XLSXRenderer) SolidarityTransportDrivers(w http.ResponseWriter, result *application.SolidarityTransportOverviewResult) { // Create Excel spreadsheet spreadsheet := r.NewSpreadsheet("Conducteurs solidaires") // Build headers dynamically based on configuration driverOptionalFields := r.Config.Get("modules.solidarity_transport.drivers.profile_optional_fields") driverFields := []string{"last_name", "first_name", "email", "phone_number", "birthdate", "gender", "file_number"} headers := []string{"ID", "Nom", "Prénom", "Email", "Téléphone", "Date de naissance", "Genre", "Numéro de dossier"} if driverOptionalFieldsList, ok := driverOptionalFields.([]interface{}); ok { for _, field := range driverOptionalFieldsList { if fieldMap, ok := field.(map[string]interface{}); ok { if name, ok := fieldMap["name"].(string); ok { driverFields = append(driverFields, name) label := name if labelVal, ok := fieldMap["label"].(string); ok { label = labelVal } headers = append(headers, label) } } } } // Add address and archived columns headers = append(headers, "Adresse", "Archivé") spreadsheet.SetHeaders(headers) // Add data rows for _, driver := range result.Accounts { row := []interface{}{} // Driver ID row = append(row, driver.ID) // Driver data for _, field := range driverFields { value := getAccountFieldValue(driver.Data, field) // Convert gender code to text if field == "gender" && value != "" { value = gender.ISO5218ToString(value) } row = append(row, value) } // Address address := "" if addr, ok := driver.Data["address"]; ok { if addrMap, ok := addr.(map[string]interface{}); ok { if props, ok := addrMap["properties"]; ok { if propsMap, ok := props.(map[string]interface{}); ok { if label, ok := propsMap["label"].(string); ok { address = label } } } } } row = append(row, address) // Archived status archived := "Non" if archivedVal, ok := driver.Data["archived"].(bool); ok && archivedVal { archived = "Oui" } row = append(row, archived) spreadsheet.AddRow(row) } // Write Excel to response w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") w.Header().Set("Content-Disposition", "attachment; filename=\"export-conducteurs-solidaires.xlsx\"") if err := spreadsheet.GetFile().Write(w); err != nil { log.Error().Err(err).Msg("Error generating Excel file") http.Error(w, "Error generating Excel file", http.StatusInternalServerError) return } }