333 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
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",
 | 
						|
		"ID Groupe",
 | 
						|
		"Statut",
 | 
						|
		"Motif de réservation",
 | 
						|
		"Raison d'annulation",
 | 
						|
		"Remplacé par (ID)",
 | 
						|
		"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.GroupId)
 | 
						|
		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)
 | 
						|
 | 
						|
		// Replaced by (from booking.Data)
 | 
						|
		replacedBy := ""
 | 
						|
		if booking.Data != nil {
 | 
						|
			if replacedByVal, ok := booking.Data["replaced_by"]; ok && replacedByVal != nil {
 | 
						|
				replacedBy = fmt.Sprint(replacedByVal)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		row = append(row, replacedBy)
 | 
						|
 | 
						|
		// 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
 | 
						|
	}
 | 
						|
}
 |