lot of new functionalities
This commit is contained in:
927
core/application/beneficiaries.go
Normal file
927
core/application/beneficiaries.go
Normal file
@@ -0,0 +1,927 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/form-validators"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
|
||||
profilepictures "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/profile-pictures"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/sorting"
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
|
||||
agenda "git.coopgo.io/coopgo-platform/agenda/grpcapi"
|
||||
agendastorage "git.coopgo.io/coopgo-platform/agenda/storage"
|
||||
"git.coopgo.io/coopgo-platform/carpool-service/servers/grpc/proto"
|
||||
fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi"
|
||||
fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage"
|
||||
groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi"
|
||||
"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"
|
||||
"git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen"
|
||||
solidaritytransformers "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/transformers"
|
||||
solidaritytypes "git.coopgo.io/coopgo-platform/solidarity-transport/types"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type BeneficiariesResult struct {
|
||||
Accounts []mobilityaccountsstorage.Account
|
||||
CacheID string
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetBeneficiaries(ctx context.Context, searchFilter string, archivedFilter bool) (*BeneficiariesResult, error) {
|
||||
accounts, err := h.getBeneficiariesWithFilters(ctx, searchFilter, archivedFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Sort(sorting.BeneficiariesByName(accounts))
|
||||
|
||||
cacheID := uuid.NewString()
|
||||
h.cache.PutWithTTL(cacheID, accounts, 1*time.Hour)
|
||||
|
||||
return &BeneficiariesResult{
|
||||
Accounts: accounts,
|
||||
CacheID: cacheID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) CreateBeneficiary(ctx context.Context, firstName, lastName, email string, birthdate *time.Time, phoneNumber, fileNumber string, address any, gender string, otherProperties any) (string, error) {
|
||||
// Get current group
|
||||
g := ctx.Value(identification.GroupKey)
|
||||
if g == nil {
|
||||
return "", fmt.Errorf("no group found in context")
|
||||
}
|
||||
group := g.(storage.Group)
|
||||
|
||||
// Create data map for the beneficiary
|
||||
dataMap := map[string]any{
|
||||
"first_name": firstName,
|
||||
"last_name": lastName,
|
||||
"email": email,
|
||||
"phone_number": phoneNumber,
|
||||
"file_number": fileNumber,
|
||||
"gender": gender,
|
||||
}
|
||||
|
||||
// Convert birthdate to string format for structpb compatibility
|
||||
if birthdate != nil {
|
||||
dataMap["birthdate"] = birthdate.Format("2006-01-02")
|
||||
}
|
||||
|
||||
if address != nil {
|
||||
dataMap["address"] = address
|
||||
}
|
||||
if otherProperties != nil {
|
||||
dataMap["other_properties"] = otherProperties
|
||||
}
|
||||
|
||||
data, err := structpb.NewValue(dataMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
request := &mobilityaccounts.RegisterRequest{
|
||||
Account: &mobilityaccounts.Account{
|
||||
Namespace: "parcoursmob_beneficiaries",
|
||||
Data: data.GetStructValue(),
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.MobilityAccounts.Register(ctx, request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
subscribe := &groupsmanagement.SubscribeRequest{
|
||||
Groupid: group.ID,
|
||||
Memberid: resp.Account.Id,
|
||||
}
|
||||
|
||||
_, err = h.services.GRPC.GroupsManagement.Subscribe(ctx, subscribe)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.Account.Id, nil
|
||||
}
|
||||
|
||||
type BeneficiaryDataResult struct {
|
||||
Account mobilityaccountsstorage.Account
|
||||
Bookings []fleetsstorage.Booking
|
||||
Organizations []any
|
||||
Documents []filestorage.FileInfo
|
||||
EventsList []Event_Beneficiary
|
||||
SolidarityTransportStats map[string]int64
|
||||
SolidarityTransportBookings []*solidaritytypes.Booking
|
||||
SolidarityDriversMap map[string]mobilityaccountsstorage.Account
|
||||
OrganizedCarpoolStats map[string]int64
|
||||
OrganizedCarpoolBookings []*proto.CarpoolServiceBooking
|
||||
OrganizedCarpoolDriversMap map[string]mobilityaccountsstorage.Account
|
||||
WalletBalance float64
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetBeneficiaryData(ctx context.Context, beneficiaryID string) (*BeneficiaryDataResult, error) {
|
||||
// Get beneficiary account
|
||||
request := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Security check: ensure this is actually a beneficiary account
|
||||
if resp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
return nil, fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, resp.Account.Namespace)
|
||||
}
|
||||
|
||||
account := resp.Account.ToStorageType()
|
||||
|
||||
// Get documents
|
||||
documents := h.filestorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + beneficiaryID)
|
||||
|
||||
// Get events subscriptions
|
||||
subscriptionRequest := &agenda.GetSubscriptionByUserRequest{
|
||||
Subscriber: beneficiaryID,
|
||||
}
|
||||
|
||||
subscriptionResp, err := h.services.GRPC.Agenda.GetSubscriptionByUser(ctx, subscriptionRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events := []agendastorage.Event{}
|
||||
currentTime := time.Now().Truncate(24 * time.Hour)
|
||||
|
||||
for _, e := range subscriptionResp.Subscription {
|
||||
eventRequest := &agenda.GetEventRequest{
|
||||
Id: e.Eventid,
|
||||
}
|
||||
eventResp, err := h.services.GRPC.Agenda.GetEvent(ctx, eventRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, eventResp.Event.ToStorageType())
|
||||
}
|
||||
|
||||
sort.Sort(sorting.EventsByStartdate(events))
|
||||
|
||||
// Get bookings
|
||||
bookingsRequest := &fleets.GetDriverBookingsRequest{
|
||||
Driver: beneficiaryID,
|
||||
}
|
||||
bookingsResp, err := h.services.GRPC.Fleets.GetDriverBookings(ctx, bookingsRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bookings := []fleetsstorage.Booking{}
|
||||
for _, b := range bookingsResp.Bookings {
|
||||
bookings = append(bookings, b.ToStorageType())
|
||||
}
|
||||
|
||||
// Build events list
|
||||
var eventsList []Event_Beneficiary
|
||||
var statusEvent int
|
||||
|
||||
for _, e := range events {
|
||||
if e.Startdate.After(currentTime) {
|
||||
statusEvent = 1
|
||||
} else if e.Startdate.Before(currentTime) && e.Enddate.After(currentTime) || e.Enddate.Equal(currentTime) {
|
||||
statusEvent = 2
|
||||
} else {
|
||||
statusEvent = 3
|
||||
}
|
||||
|
||||
event := Event{
|
||||
NameVal: e.Name,
|
||||
DateVal: e.Startdate,
|
||||
DateEndVal: e.Enddate,
|
||||
TypeVal: e.Type,
|
||||
IDVal: e.ID,
|
||||
DbVal: "/app/agenda/",
|
||||
IconSet: "calendar",
|
||||
StatusVal: statusEvent,
|
||||
}
|
||||
|
||||
eventsList = append(eventsList, event)
|
||||
}
|
||||
|
||||
// Add vehicle bookings to events list
|
||||
var statusBooking int
|
||||
for _, b := range bookings {
|
||||
if b.Enddate.After(currentTime) || b.Enddate.Equal(currentTime) {
|
||||
getVehicleRequest := &fleets.GetVehicleRequest{
|
||||
Vehicleid: b.Vehicleid,
|
||||
}
|
||||
|
||||
getVehicleResp, err := h.services.GRPC.Fleets.GetVehicle(ctx, getVehicleRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.Startdate.After(currentTime) {
|
||||
statusBooking = 1
|
||||
} else if b.Startdate.Before(currentTime) && b.Enddate.After(currentTime) || b.Enddate.Equal(currentTime) {
|
||||
statusBooking = 2
|
||||
} else {
|
||||
statusBooking = 3
|
||||
}
|
||||
|
||||
event := Event{
|
||||
NameVal: getVehicleResp.Vehicle.ToStorageType().Data["name"].(string),
|
||||
DateVal: b.Startdate,
|
||||
DateEndVal: b.Enddate,
|
||||
TypeVal: "Réservation de véhicule",
|
||||
IDVal: b.ID,
|
||||
DbVal: "/app/vehicles-management/bookings/",
|
||||
IconSet: "vehicle",
|
||||
StatusVal: statusBooking,
|
||||
}
|
||||
|
||||
eventsList = append(eventsList, event)
|
||||
}
|
||||
}
|
||||
|
||||
// Get solidarity transport bookings (all statuses for display)
|
||||
solidarityResp, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBookings(ctx, &gen.GetSolidarityTransportBookingsRequest{
|
||||
Passengerid: beneficiaryID,
|
||||
StartDate: timestamppb.New(time.Now().Add(-365 * 24 * time.Hour)),
|
||||
EndDate: timestamppb.New(time.Now().Add(365 * 24 * time.Hour)),
|
||||
})
|
||||
|
||||
protoBookings := []*gen.SolidarityTransportBooking{}
|
||||
if err == nil {
|
||||
protoBookings = solidarityResp.Bookings
|
||||
} else {
|
||||
log.Error().Err(err).Msg("error retrieving solidarity transport bookings for beneficiary")
|
||||
}
|
||||
|
||||
// Convert proto bookings to types with geojson.Feature
|
||||
solidarityTransportBookings := []*solidaritytypes.Booking{}
|
||||
for _, protoBooking := range protoBookings {
|
||||
booking, err := solidaritytransformers.BookingProtoToType(protoBooking)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error converting booking proto to type")
|
||||
continue
|
||||
}
|
||||
solidarityTransportBookings = append(solidarityTransportBookings, booking)
|
||||
}
|
||||
|
||||
// Collect unique driver IDs
|
||||
driverIDs := []string{}
|
||||
driverIDsMap := make(map[string]bool)
|
||||
for _, booking := range solidarityTransportBookings {
|
||||
if booking.DriverId != "" {
|
||||
if !driverIDsMap[booking.DriverId] {
|
||||
driverIDs = append(driverIDs, booking.DriverId)
|
||||
driverIDsMap[booking.DriverId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get drivers in batch
|
||||
driversMap := make(map[string]mobilityaccountsstorage.Account)
|
||||
if len(driverIDs) > 0 {
|
||||
driversResp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(ctx, &mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: driverIDs,
|
||||
})
|
||||
if err == nil {
|
||||
for _, account := range driversResp.Accounts {
|
||||
a := account.ToStorageType()
|
||||
driversMap[a.ID] = a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate stats only for validated bookings
|
||||
solidarityTransportStats := map[string]int64{
|
||||
"count": 0,
|
||||
"km": 0,
|
||||
}
|
||||
|
||||
for _, b := range solidarityTransportBookings {
|
||||
if b.Status == "VALIDATED" {
|
||||
solidarityTransportStats["count"] = solidarityTransportStats["count"] + 1
|
||||
if b.Journey != nil {
|
||||
solidarityTransportStats["km"] = solidarityTransportStats["km"] + b.Journey.PassengerDistance
|
||||
}
|
||||
|
||||
// Add to events list
|
||||
event := Event{
|
||||
NameVal: fmt.Sprintf("%s (%d km)", b.Journey.PassengerDrop.Properties.MustString("label", ""), b.Journey.PassengerDistance),
|
||||
DateVal: b.Journey.PassengerPickupDate,
|
||||
DateEndVal: b.Journey.PassengerPickupDate,
|
||||
TypeVal: "Transport solidaire",
|
||||
IDVal: b.Id,
|
||||
DbVal: "/app/solidarity-transport/bookings/",
|
||||
IconSet: "vehicle",
|
||||
StatusVal: 1,
|
||||
}
|
||||
|
||||
eventsList = append(eventsList, event)
|
||||
}
|
||||
}
|
||||
|
||||
// Get organized carpool bookings
|
||||
carpoolBookingsResp, err := h.services.GRPC.CarpoolService.GetUserBookings(ctx, &proto.GetUserBookingsRequest{
|
||||
UserId: beneficiaryID,
|
||||
})
|
||||
|
||||
organizedCarpoolBookings := []*proto.CarpoolServiceBooking{}
|
||||
if err == nil {
|
||||
organizedCarpoolBookings = carpoolBookingsResp.Bookings
|
||||
} else {
|
||||
log.Error().Err(err).Msg("error retrieving organized carpool bookings for beneficiary")
|
||||
}
|
||||
|
||||
// Collect unique driver IDs from organized carpool bookings
|
||||
carpoolDriverIDs := []string{}
|
||||
carpoolDriverIDsMap := make(map[string]bool)
|
||||
for _, booking := range organizedCarpoolBookings {
|
||||
if booking.Driver != nil && booking.Driver.Id != "" {
|
||||
if !carpoolDriverIDsMap[booking.Driver.Id] {
|
||||
carpoolDriverIDs = append(carpoolDriverIDs, booking.Driver.Id)
|
||||
carpoolDriverIDsMap[booking.Driver.Id] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get organized carpool drivers in batch
|
||||
organizedCarpoolDriversMap := make(map[string]mobilityaccountsstorage.Account)
|
||||
if len(carpoolDriverIDs) > 0 {
|
||||
carpoolDriversResp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(ctx, &mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: carpoolDriverIDs,
|
||||
})
|
||||
if err == nil {
|
||||
for _, account := range carpoolDriversResp.Accounts {
|
||||
a := account.ToStorageType()
|
||||
organizedCarpoolDriversMap[a.ID] = a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate organized carpool stats (only confirmed bookings)
|
||||
organizedCarpoolStats := map[string]int64{
|
||||
"count": 0,
|
||||
"km": 0,
|
||||
}
|
||||
|
||||
for _, cb := range organizedCarpoolBookings {
|
||||
if cb.Status == proto.CarpoolServiceBookingStatus_CONFIRMED {
|
||||
organizedCarpoolStats["count"]++
|
||||
if cb.Distance != nil {
|
||||
organizedCarpoolStats["km"] += *cb.Distance
|
||||
}
|
||||
|
||||
// Build journey name from drop address and distance for events
|
||||
journeyName := "Covoiturage"
|
||||
if cb.PassengerDropAddress != nil {
|
||||
if cb.Distance != nil {
|
||||
journeyName = fmt.Sprintf("%s (%d km)", *cb.PassengerDropAddress, *cb.Distance)
|
||||
} else {
|
||||
journeyName = *cb.PassengerDropAddress
|
||||
}
|
||||
}
|
||||
|
||||
// Get departure date
|
||||
departureDate := time.Now()
|
||||
if cb.PassengerPickupDate != nil {
|
||||
departureDate = cb.PassengerPickupDate.AsTime()
|
||||
}
|
||||
|
||||
event := Event{
|
||||
NameVal: journeyName,
|
||||
DateVal: departureDate,
|
||||
DateEndVal: departureDate,
|
||||
TypeVal: "Covoiturage solidaire",
|
||||
IDVal: cb.Id,
|
||||
DbVal: "/app/organized-carpool/bookings/",
|
||||
IconSet: "vehicle",
|
||||
StatusVal: 1,
|
||||
}
|
||||
|
||||
eventsList = append(eventsList, event)
|
||||
}
|
||||
}
|
||||
|
||||
sortByDate(eventsList)
|
||||
|
||||
// Get organizations
|
||||
groupsRequest := &groupsmanagement.GetGroupsRequest{
|
||||
Namespaces: []string{"parcoursmob_organizations"},
|
||||
Member: beneficiaryID,
|
||||
}
|
||||
|
||||
groupsResp, err := h.services.GRPC.GroupsManagement.GetGroups(ctx, groupsRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
organizations := []any{}
|
||||
for _, o := range groupsResp.Groups {
|
||||
organizations = append(organizations, o.ToStorageType())
|
||||
}
|
||||
|
||||
// Calculate wallet balance
|
||||
walletBalance := h.calculateWalletBalance(account)
|
||||
|
||||
return &BeneficiaryDataResult{
|
||||
Account: account,
|
||||
Bookings: bookings,
|
||||
Organizations: organizations,
|
||||
Documents: documents,
|
||||
EventsList: eventsList,
|
||||
SolidarityTransportStats: solidarityTransportStats,
|
||||
SolidarityTransportBookings: solidarityTransportBookings,
|
||||
SolidarityDriversMap: driversMap,
|
||||
OrganizedCarpoolStats: organizedCarpoolStats,
|
||||
OrganizedCarpoolBookings: organizedCarpoolBookings,
|
||||
OrganizedCarpoolDriversMap: organizedCarpoolDriversMap,
|
||||
WalletBalance: walletBalance,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type BeneficiaryResult struct {
|
||||
Account mobilityaccountsstorage.Account
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetBeneficiary(ctx context.Context, beneficiaryID string) (*BeneficiaryResult, error) {
|
||||
request := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Security check: ensure this is actually a beneficiary account
|
||||
if resp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
return nil, fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, resp.Account.Namespace)
|
||||
}
|
||||
|
||||
return &BeneficiaryResult{
|
||||
Account: resp.Account.ToStorageType(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) UpdateBeneficiary(ctx context.Context, beneficiaryID, firstName, lastName, email string, birthdate *time.Time, phoneNumber, fileNumber string, address any, gender string, otherProperties any) (string, error) {
|
||||
// Security check: verify the account exists and is a beneficiary
|
||||
getRequest := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
getResp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, getRequest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if getResp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
return "", fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, getResp.Account.Namespace)
|
||||
}
|
||||
|
||||
// Create data map for the beneficiary
|
||||
dataMap := map[string]any{
|
||||
"first_name": firstName,
|
||||
"last_name": lastName,
|
||||
"email": email,
|
||||
"phone_number": phoneNumber,
|
||||
"file_number": fileNumber,
|
||||
"gender": gender,
|
||||
}
|
||||
|
||||
// Handle birthdate conversion for protobuf compatibility
|
||||
if birthdate != nil {
|
||||
dataMap["birthdate"] = birthdate.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
if address != nil {
|
||||
dataMap["address"] = address
|
||||
}
|
||||
if otherProperties != nil {
|
||||
dataMap["other_properties"] = otherProperties
|
||||
}
|
||||
|
||||
data, err := structpb.NewValue(dataMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
request := &mobilityaccounts.UpdateDataRequest{
|
||||
Account: &mobilityaccounts.Account{
|
||||
Id: beneficiaryID,
|
||||
Namespace: "parcoursmob_beneficiaries",
|
||||
Data: data.GetStructValue(),
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.MobilityAccounts.UpdateData(ctx, request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.Account.Id, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) ArchiveBeneficiary(ctx context.Context, beneficiaryID string) error {
|
||||
// Security check: verify the account exists and is a beneficiary
|
||||
getRequest := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
getResp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, getRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if getResp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
return fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, getResp.Account.Namespace)
|
||||
}
|
||||
|
||||
data, err := structpb.NewValue(map[string]any{
|
||||
"archived": true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := &mobilityaccounts.UpdateDataRequest{
|
||||
Account: &mobilityaccounts.Account{
|
||||
Id: beneficiaryID,
|
||||
Namespace: "parcoursmob_beneficiaries",
|
||||
Data: data.GetStructValue(),
|
||||
},
|
||||
}
|
||||
|
||||
_, err = h.services.GRPC.MobilityAccounts.UpdateData(ctx, request)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) UnarchiveBeneficiary(ctx context.Context, beneficiaryID string) error {
|
||||
// Security check: verify the account exists and is a beneficiary
|
||||
getRequest := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
getResp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, getRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if getResp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
return fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, getResp.Account.Namespace)
|
||||
}
|
||||
|
||||
data, err := structpb.NewValue(map[string]any{
|
||||
"archived": false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := &mobilityaccounts.UpdateDataRequest{
|
||||
Account: &mobilityaccounts.Account{
|
||||
Id: beneficiaryID,
|
||||
Namespace: "parcoursmob_beneficiaries",
|
||||
Data: data.GetStructValue(),
|
||||
},
|
||||
}
|
||||
|
||||
_, err = h.services.GRPC.MobilityAccounts.UpdateData(ctx, request)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetBeneficiaryPicture(ctx context.Context, beneficiaryID string) ([]byte, string, error) {
|
||||
request := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, request)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Security check: ensure this is actually a beneficiary account
|
||||
// if resp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
// return nil, "", fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, resp.Account.Namespace)
|
||||
// }
|
||||
|
||||
account := resp.Account.ToStorageType()
|
||||
|
||||
firstName, ok := account.Data["first_name"].(string)
|
||||
if !ok || firstName == "" {
|
||||
firstName = "U"
|
||||
}
|
||||
lastName, ok := account.Data["last_name"].(string)
|
||||
if !ok || lastName == "" {
|
||||
lastName = "U"
|
||||
}
|
||||
|
||||
initials := strings.ToUpper(string(firstName[0]) + string(lastName[0]))
|
||||
picture := profilepictures.DefaultProfilePicture(initials)
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
if err := png.Encode(buffer, picture); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return buffer.Bytes(), "image/png", nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) AddBeneficiaryDocument(ctx context.Context, beneficiaryID string, file io.Reader, filename string, fileSize int64, documentType, documentName string) error {
|
||||
// Security check: verify the account exists and is a beneficiary
|
||||
getRequest := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
getResp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, getRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if getResp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
return fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, getResp.Account.Namespace)
|
||||
}
|
||||
|
||||
fileid := uuid.NewString()
|
||||
|
||||
metadata := map[string]string{
|
||||
"type": documentType,
|
||||
"name": documentName,
|
||||
}
|
||||
|
||||
if err := h.filestorage.Put(file, filestorage.PREFIX_BENEFICIARIES, fmt.Sprintf("%s/%s_%s", beneficiaryID, fileid, filename), fileSize, metadata); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetBeneficiaryDocument(ctx context.Context, beneficiaryID, document string) (io.Reader, *filestorage.FileInfo, error) {
|
||||
// Security check: verify the account exists and is a beneficiary
|
||||
getRequest := &mobilityaccounts.GetAccountRequest{
|
||||
Id: beneficiaryID,
|
||||
}
|
||||
getResp, err := h.services.GRPC.MobilityAccounts.GetAccount(ctx, getRequest)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if getResp.Account.Namespace != "parcoursmob_beneficiaries" {
|
||||
return nil, nil, fmt.Errorf("account %s is not a beneficiary (namespace: %s)", beneficiaryID, getResp.Account.Namespace)
|
||||
}
|
||||
|
||||
file, info, err := h.filestorage.Get(filestorage.PREFIX_BENEFICIARIES, fmt.Sprintf("%s/%s", beneficiaryID, document))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return file, info, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) DeleteBeneficiaryDocument(ctx context.Context, beneficiaryID, document string) error {
|
||||
return h.DeleteDocument(ctx, BeneficiaryDocumentConfig, beneficiaryID, document)
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) getBeneficiariesWithFilters(ctx context.Context, searchFilter string, archivedFilter bool) ([]mobilityaccountsstorage.Account, error) {
|
||||
accounts := []mobilityaccountsstorage.Account{}
|
||||
g := ctx.Value(identification.GroupKey)
|
||||
if g == nil {
|
||||
return accounts, errors.New("no group provided")
|
||||
}
|
||||
|
||||
group := g.(storage.Group)
|
||||
|
||||
request := &mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: group.Members,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(ctx, request)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("issue in mobilityaccounts call")
|
||||
return accounts, err
|
||||
}
|
||||
|
||||
for _, account := range resp.Accounts {
|
||||
if h.filterAccount(account, searchFilter, archivedFilter) {
|
||||
a := account.ToStorageType()
|
||||
accounts = append(accounts, a)
|
||||
}
|
||||
}
|
||||
|
||||
return accounts, err
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) filterAccount(a *mobilityaccounts.Account, searchFilter string, archivedFilter bool) bool {
|
||||
// Search filter
|
||||
if searchFilter != "" {
|
||||
name := a.Data.AsMap()["first_name"].(string) + " " + a.Data.AsMap()["last_name"].(string)
|
||||
if !strings.Contains(strings.ToLower(name), strings.ToLower(searchFilter)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Archived filter
|
||||
if archivedFilter {
|
||||
if archived, ok := a.Data.AsMap()["archived"].(bool); ok && archived {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
if archived, ok := a.Data.AsMap()["archived"].(bool); ok && archived {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type Event_Beneficiary interface {
|
||||
Name() string
|
||||
Date() time.Time
|
||||
DateEnd() time.Time
|
||||
Type() string
|
||||
Db() string
|
||||
ID() string
|
||||
Icons() string
|
||||
Status() int
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
IDVal string
|
||||
NameVal string
|
||||
DateVal time.Time
|
||||
DateEndVal time.Time
|
||||
TypeVal string
|
||||
DbVal string
|
||||
Deleted bool
|
||||
IconSet string
|
||||
StatusVal int
|
||||
}
|
||||
|
||||
func (e Event) Name() string {
|
||||
return e.NameVal
|
||||
}
|
||||
|
||||
func (e Event) Date() time.Time {
|
||||
return e.DateVal
|
||||
}
|
||||
|
||||
func (e Event) DateEnd() time.Time {
|
||||
return e.DateEndVal
|
||||
}
|
||||
|
||||
func (e Event) Type() string {
|
||||
return e.TypeVal
|
||||
}
|
||||
|
||||
func (e Event) ID() string {
|
||||
return e.IDVal
|
||||
}
|
||||
|
||||
func (e Event) Db() string {
|
||||
return e.DbVal
|
||||
}
|
||||
|
||||
func (e Event) Icons() string {
|
||||
return e.IconSet
|
||||
}
|
||||
|
||||
func (e Event) Status() int {
|
||||
return e.StatusVal
|
||||
}
|
||||
|
||||
func sortByDate(events []Event_Beneficiary) {
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
return events[i].Date().After(events[j].Date())
|
||||
})
|
||||
}
|
||||
|
||||
// Utility functions needed by other modules
|
||||
func filterAccount(r *http.Request, a *mobilityaccounts.Account) bool {
|
||||
searchFilter, ok := r.URL.Query()["search"]
|
||||
|
||||
if ok && len(searchFilter[0]) > 0 {
|
||||
name := a.Data.AsMap()["first_name"].(string) + " " + a.Data.AsMap()["last_name"].(string)
|
||||
if !strings.Contains(strings.ToLower(name), strings.ToLower(searchFilter[0])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
archivedFilter, ok := r.URL.Query()["archived"]
|
||||
if ok && archivedFilter[0] == "true" {
|
||||
if archived, ok := a.Data.AsMap()["archived"].(bool); ok && archived {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
if archived, ok := a.Data.AsMap()["archived"].(bool); ok && archived {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) beneficiaries(r *http.Request) ([]mobilityaccountsstorage.Account, error) {
|
||||
accounts := []mobilityaccountsstorage.Account{}
|
||||
g := r.Context().Value(identification.GroupKey)
|
||||
if g == nil {
|
||||
return accounts, errors.New("no group provided")
|
||||
}
|
||||
|
||||
group := g.(storage.Group)
|
||||
|
||||
request := &mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: group.Members,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(context.TODO(), request)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("issue in mobilityaccounts call")
|
||||
return accounts, err
|
||||
}
|
||||
|
||||
for _, account := range resp.Accounts {
|
||||
if filterAccount(r, account) {
|
||||
a := account.ToStorageType()
|
||||
accounts = append(accounts, a)
|
||||
}
|
||||
}
|
||||
|
||||
return accounts, err
|
||||
}
|
||||
|
||||
type BeneficiariesForm struct {
|
||||
FirstName string `json:"first_name" validate:"required"`
|
||||
LastName string `json:"last_name" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Birthdate *time.Time `json:"birthdate" validate:"required"`
|
||||
PhoneNumber string `json:"phone_number" validate:"required,phoneNumber"`
|
||||
FileNumber string `json:"file_number"`
|
||||
Address any `json:"address,omitempty"`
|
||||
Gender string `json:"gender"`
|
||||
OtherProperties any `json:"other_properties,omitempty"`
|
||||
}
|
||||
|
||||
func parseBeneficiariesForm(r *http.Request) (map[string]any, error) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var date *time.Time
|
||||
|
||||
if r.PostFormValue("birthdate") != "" {
|
||||
d, err := time.Parse("2006-01-02", r.PostFormValue("birthdate"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
date = &d
|
||||
}
|
||||
|
||||
formData := BeneficiariesForm{
|
||||
FirstName: r.PostFormValue("first_name"),
|
||||
LastName: r.PostFormValue("last_name"),
|
||||
Email: r.PostFormValue("email"),
|
||||
Birthdate: date,
|
||||
PhoneNumber: r.PostFormValue("phone_number"),
|
||||
FileNumber: r.PostFormValue("file_number"),
|
||||
Gender: r.PostFormValue("gender"),
|
||||
}
|
||||
|
||||
if r.PostFormValue("address") != "" {
|
||||
var a any
|
||||
json.Unmarshal([]byte(r.PostFormValue("address")), &a)
|
||||
formData.Address = a
|
||||
}
|
||||
|
||||
if r.PostFormValue("other_properties") != "" {
|
||||
var a any
|
||||
json.Unmarshal([]byte(r.PostFormValue("other_properties")), &a)
|
||||
formData.OtherProperties = a
|
||||
}
|
||||
|
||||
validate := formvalidators.New()
|
||||
if err := validate.Struct(formData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d, err := json.Marshal(formData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dataMap map[string]any
|
||||
err = json.Unmarshal(d, &dataMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dataMap, nil
|
||||
}
|
||||
Reference in New Issue
Block a user