parcoursmob/core/application/beneficiaries.go

928 lines
26 KiB
Go

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
}