github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/client/controllers/cont_camps.go (about) 1 package controllers 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "sort" 8 "strconv" 9 "time" 10 11 "github.com/benoitkugler/goACVE/server/core/apiserver" 12 dm "github.com/benoitkugler/goACVE/server/core/datamodel" 13 "github.com/benoitkugler/goACVE/server/core/documents" 14 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 15 "github.com/benoitkugler/goACVE/server/core/utils/table" 16 ) 17 18 var HeadersResponsables = []rd.Header{ 19 {Field: dm.ParticipantRespoNom, Label: "Nom"}, 20 {Field: dm.ParticipantRespoPrenom, Label: "Prénom"}, 21 {Field: dm.ParticipantRespoMail, Label: "Mail"}, 22 {Field: dm.ParticipantRespoTelsLines, Label: "Téléphones"}, 23 {Field: dm.ParticipantRespoAdresse, Label: "Adresse"}, 24 {Field: dm.ParticipantRespoCodePostal, Label: "Code postal"}, 25 {Field: dm.ParticipantRespoVille, Label: "Ville"}, 26 } 27 28 type ButtonsCamps struct { 29 CreerParticipant, SupprimerParticipant, ChangerParticipant, VerifieAttente, ExporterParticipants EtatSideButton 30 } 31 32 type OngletCamps interface { 33 baseOnglet 34 35 ConfirmeNotifPlaceLiberee(info string) (keep bool, notifie bool) 36 ConfirmeAjoutParticipant(camp dm.AccesCamp, personne rd.Personne, causes []string) bool 37 ConfirmeSupprimeParticipant(msg string) bool 38 } 39 40 type EtatCamps struct { 41 CampCurrent rd.IId 42 ShowCampsTerminated bool 43 44 IdParticipant rd.IId // simple ou complet 45 } 46 47 type Camps struct { 48 Onglet OngletCamps 49 main *MainController 50 ListeCamps rd.Table 51 ListeInscrits rd.Table 52 ListeAttente rd.Table 53 HeaderInscrits []rd.Header 54 HeaderAttente []rd.Header 55 Base *dm.BaseLocale 56 Etat EtatCamps 57 58 // Droit d'édition/ajout participant, 59 // Droit de modification partielle des infos du camp 60 ParticipantsRight, CampsRight bool 61 } 62 63 func NewCamps(main *MainController, permission int) *Camps { 64 c := Camps{main: main, Base: main.Base, ParticipantsRight: permission >= 2, 65 CampsRight: permission >= 3, 66 HeaderAttente: []rd.Header{ 67 {Field: dm.PersonneNom, Label: "Nom"}, 68 {Field: dm.PersonnePrenom, Label: "Prénom"}, 69 {Field: dm.PersonneSexe, Label: "Sexe"}, 70 {Field: dm.ParticipantAgeDebutCamp, Label: "Age au début du camp"}, 71 {Field: dm.PersonneDateNaissance, Label: "Date de naissance"}, 72 {Field: dm.ParticipantRaisonAttente, Label: "Statut"}, 73 }, 74 HeaderInscrits: []rd.Header{ 75 {Field: dm.PersonneNom, Label: "Nom"}, 76 {Field: dm.PersonnePrenom, Label: "Prénom"}, 77 {Field: dm.PersonneSexe, Label: "Sexe"}, 78 {Field: dm.ParticipantAgeDebutCamp, Label: "Age au début du camp"}, 79 {Field: dm.PersonneDateNaissance, Label: "Date de naissance"}, 80 {Field: dm.ParticipantGroupe, Label: "Groupe"}, 81 {Field: dm.ParticipantBus, Label: "Navette"}, 82 {Field: dm.ParticipantMaterielSki, Label: "Matériel de ski"}, 83 }, 84 } 85 c.resetData() 86 return &c 87 } 88 89 func (c *Camps) resetData() { 90 isTerminated := rd.OBNon 91 if c.Etat.ShowCampsTerminated { 92 isTerminated = 0 93 } 94 tmp := c.Base.GetCamps(false, isTerminated) 95 sort.Slice(tmp, func(i, j int) bool { // ordre déterministique 96 return tmp[i].Id < tmp[j].Id 97 }) 98 sort.SliceStable(tmp, func(i, j int) bool { 99 return tmp[i].RawData().DateDebut.Time().Before(tmp[j].RawData().DateDebut.Time()) 100 }) 101 sort.SliceStable(tmp, func(i, j int) bool { 102 return bool(tmp[i].RawData().Ouvert || !tmp[j].RawData().Ouvert) 103 }) 104 105 _, cache1 := c.Base.ResoudParticipants() 106 cache2 := c.Base.ResoudParticipantsimples() 107 c.ListeCamps = make(rd.Table, len(tmp)) 108 for index, camp := range tmp { 109 c.ListeCamps[index] = camp.AsItem(cache1, cache2) 110 } 111 112 // besoin de déselectionner si absent des camps affichés 113 if c.Etat.CampCurrent != nil && !HasId(c.ListeCamps, c.Etat.CampCurrent) { 114 c.Etat.CampCurrent = nil 115 } 116 117 partCurrentInListe := false 118 if idCamp := c.Etat.CampCurrent; idCamp != nil { 119 camp := c.Base.NewCamp(idCamp.Int64()) 120 c.ListeInscrits, c.ListeAttente = camp.GetListes(false, "", false, false) 121 partCurrent := c.Etat.IdParticipant 122 if partCurrent != nil && (HasId(c.ListeInscrits, partCurrent) || HasId(c.ListeAttente, partCurrent)) { 123 partCurrentInListe = true 124 } 125 } 126 // besoin de déselectionner si absent des participants affichés 127 if !partCurrentInListe { 128 c.Etat.IdParticipant = nil 129 } 130 } 131 132 func (c *Camps) SideButtons() ButtonsCamps { 133 bs := ButtonsCamps{} 134 hasCamp := ButtonPresent 135 if c.Etat.CampCurrent != nil { 136 hasCamp = ButtonActivated 137 } 138 if c.ParticipantsRight { 139 bs.CreerParticipant = hasCamp 140 141 bs.SupprimerParticipant = ButtonPresent 142 bs.ChangerParticipant = ButtonPresent 143 if c.Etat.IdParticipant != nil { 144 bs.SupprimerParticipant = ButtonActivated 145 bs.ChangerParticipant = ButtonActivated 146 } 147 } 148 bs.ExporterParticipants = hasCamp 149 bs.VerifieAttente = ButtonActivated 150 return bs 151 } 152 153 func (c *Camps) GetStats() (out []Stat) { 154 if c.Etat.CampCurrent == nil { 155 return 156 } 157 camp := c.Base.NewCamp(c.Etat.CampCurrent.Int64()) 158 stats := camp.GetStats(nil, nil, nil) 159 out = []Stat{ 160 {Label: "Nombre d'inscrits", Value: strconv.Itoa(stats.Inscrits)}, 161 {Label: "Nombre de places restantes", Value: strconv.Itoa(stats.PlacesRestantes)}, 162 {Label: "Taille de la liste d'attente", Value: strconv.Itoa(stats.Attente)}, 163 {Label: "Taille de l'équipe", Value: strconv.Itoa(stats.Equipe)}, 164 {Label: "Nombre de places réservées", Value: strconv.Itoa(stats.PlacesReservees)}, 165 } 166 if camp.RawData().NeedEquilibreGf { 167 v := "-" 168 if stats.Inscrits != 0 { 169 pG := stats.Garcons * 100 / stats.Inscrits 170 v = fmt.Sprintf("%d/%d (%%)", pG, 100-pG) 171 } 172 out = append(out, Stat{Label: "Equilibre Garçons/Filles", Value: v}) 173 } 174 return 175 } 176 177 func updateCamp(camp rd.Camp) (rd.Camp, error) { 178 var out rd.Camp 179 err := requete(apiserver.UrlCamps, http.MethodPost, camp, &out) 180 return out, err 181 } 182 183 // UpdateCamp met à jour le profil transmis sur le serveur 184 func (c *Camps) UpdateCamp(camp rd.Camp) { 185 job := func() (interface{}, error) { 186 return updateCamp(camp) 187 } 188 onSuccess := func(_out interface{}) { 189 out := _out.(rd.Camp) 190 c.Base.Camps[out.Id] = out 191 c.main.ShowStandard(fmt.Sprintf("Camp %s mis à jour avec succès.", out.Label()), false) 192 c.main.ResetAllControllers() 193 } 194 c.main.ShowStandard("Mise à jour du camp...", true) 195 c.main.Background.Run(job, onSuccess) 196 } 197 198 // UpdateParticipant met à jour de profil transmis sur le serveur, de manière SYNCHRONE. 199 // Gère l'erreur et la modification de la base locale. 200 func (c *Camps) UpdateParticipant(part rd.Participant) *dm.AccesParticipant { 201 c.main.ShowStandard("Mise à jour du participant...", true) 202 var out apiserver.CreateParticipantOut 203 if err := requete(apiserver.UrlParticipants, http.MethodPost, part, &out); err != nil { 204 c.main.ShowError(err) 205 return nil 206 } 207 208 c.Base.ApplyCreateParticipant(out) 209 c.main.ShowStandard("Participant mis à jour avec succès.", false) 210 c.main.ResetAllControllers() 211 ac := c.Base.NewParticipant(out.Participant.Id) 212 return &ac 213 } 214 215 // UpdateParticipantsimple met à jour de profil transmis sur le serveur, de manière SYNCHRONE. 216 // Gère l'erreur et la modification de la base locale. 217 func (c *Camps) UpdateParticipantsimple(part rd.Participantsimple) *dm.AccesParticipantsimple { 218 c.main.ShowStandard("Mise à jour du participant...", true) 219 var out rd.Participantsimple 220 if err := requete(apiserver.UrlParticipantsimples, http.MethodPost, part, &out); err != nil { 221 c.main.ShowError(err) 222 return nil 223 } 224 225 c.Base.Participantsimples[out.Id] = out 226 c.main.ShowStandard("Participant mis à jour avec succès.", false) 227 c.main.ResetAllControllers() 228 ac := c.Base.NewParticipantsimple(out.Id) 229 return &ac 230 } 231 232 func (c *Camps) SupprimeParticipantCourant() { 233 switch idPart := c.Etat.IdParticipant.(type) { 234 case rd.IdParticipant: 235 c.SupprimeParticipant(c.Base.NewParticipant(idPart.Int64()), false) 236 case rd.IdParticipantsimple: 237 c.supprimeParticipantsimple(c.Base.NewParticipantsimple(idPart.Int64())) 238 } 239 } 240 241 // SupprimeParticipant demande la suppression au serveur, 242 // après avoir averti l'utilisateur. 243 // Effectue un reset général. 244 func (c *Camps) SupprimeParticipant(part dm.AccesParticipant, synchrone bool) { 245 message := fmt.Sprintf("Suppression de <b>%s</b> du camp %s <br/><br/>", part.GetPersonne().RawData().NomPrenom(), 246 part.GetCamp().RawData().Label()) 247 248 fac, hasFacture := part.GetFacture() 249 if hasFacture { 250 message += "Le participant sera <b>supprimé et retiré du dossier</b> associé. <br/>" 251 if len(fac.GetDossiers(nil)) == 1 { // dernier dossier 252 message += "Attention, le dossier sera vide. Si vous pensez ne plus" + 253 " travailler avec ce dossier, pensez à le supprimer (Onglet Suivi des dossiers). <br/>" 254 } 255 } else { 256 message += "Le participant sera <b>supprimé</b>. <br/>" 257 } 258 aides := part.GetAides(nil) 259 if len(aides) == 1 { 260 message += "<i>Une aide liée</i> au participant sera aussi supprimée." 261 } else if len(aides) > 1 { 262 message += fmt.Sprintf("<br/> <i>%d aides liées</i> au participant seront aussi supprimées.", len(aides)) 263 } 264 265 if !c.Onglet.ConfirmeSupprimeParticipant(message) { 266 c.main.ShowStandard("Suppression annulée.", false) 267 return 268 } 269 270 job := func() (interface{}, error) { 271 var out apiserver.DeleteParticipantOut 272 err := requete(apiserver.UrlParticipants, http.MethodDelete, IdAsParams(part.Id), &out) 273 return out, err 274 } 275 onSuccess := func(_out interface{}) { 276 out := _out.(apiserver.DeleteParticipantOut) 277 c.Base.CleanupParticipant(out) 278 msg := "Participant supprimé avec succès." 279 if len(out.IdsAides) > 0 { 280 msg += fmt.Sprintf(" %d aide(s) associées également supprimées.", len(out.IdsAides)) 281 } 282 if out.IdPersonne >= 0 { 283 msg += " Personne temporaire supprimée." 284 } 285 c.main.ShowStandard(msg, false) 286 c.main.ResetAllControllers() 287 } 288 c.main.ShowStandard("Suppression du participant...", true) 289 background := c.main.Background 290 if synchrone { 291 background = SequentialBackground{OnError: c.main.ShowError} 292 } 293 background.Run(job, onSuccess) 294 } 295 296 // supprimeParticipantsimple demande la suppression au serveur (asynchrone), 297 // après avoir averti l'utilisateur. 298 // Effectue un reset général. 299 func (c *Camps) supprimeParticipantsimple(part dm.AccesParticipantsimple) bool { 300 message := fmt.Sprintf("Suppression de <b>%s</b> du camp %s <br/><br/>", part.GetPersonne().RawData().NomPrenom(), 301 part.GetCamp().RawData().Label()) 302 303 if !c.Onglet.ConfirmeSupprimeParticipant(message) { 304 c.main.ShowStandard("Suppression annulée.", false) 305 return false 306 } 307 308 job := func() (interface{}, error) { 309 var out rd.Participantsimple 310 err := requete(apiserver.UrlParticipantsimples, http.MethodDelete, IdAsParams(part.Id), &out) 311 return out, err 312 } 313 onSuccess := func(_out interface{}) { 314 out := _out.(rd.Participantsimple) 315 delete(c.Base.Participantsimples, out.Id) 316 c.main.ShowStandard("Participant supprimé avec succès.", false) 317 c.main.ResetAllControllers() 318 319 } 320 c.main.ShowStandard("Suppression du participant...", true) 321 c.main.Background.Run(job, onSuccess) 322 return true 323 } 324 325 func (c *Camps) checkParticipant(participant rd.Participant) (keep bool) { 326 personne := c.Base.Personnes[participant.IdPersonne] 327 camp := c.Base.NewCamp(participant.IdCamp) 328 if c.Base.IsParticipantAlreadyHere(personne.Id, camp.Id) { 329 c.main.ShowError(fmt.Errorf("%s est déjà présent(e) au camp %s", personne.NomPrenom(), 330 camp.RawData().Label())) 331 return false 332 } 333 hints := camp.HintsAttente(participant.IdPersonne, participant.ListeAttente.Statut == rd.Inscrit) 334 if hints.Hint() != rd.Inscrit && !c.Onglet.ConfirmeAjoutParticipant(camp, personne, hints.Causes()) { 335 c.main.ShowStandard("Ajout annulé.", false) 336 return false 337 } 338 return true 339 } 340 341 // CreeParticipant ajoute le participant donné, après vérification 342 func (c *Camps) CreeParticipant(participant rd.Participant) { 343 if !c.checkParticipant(participant) { 344 return 345 } 346 347 participant.DateHeure = rd.Time(time.Now()) 348 job := func() (interface{}, error) { 349 var out apiserver.CreateParticipantOut 350 err := requete(apiserver.UrlParticipants, http.MethodPut, participant, &out) 351 return out, err 352 } 353 onSuccess := func(_out interface{}) { 354 out := _out.(apiserver.CreateParticipantOut) 355 c.Base.ApplyCreateParticipant(out) 356 c.main.ShowStandard("Participant ajouté avec succès.", false) 357 c.main.ResetAllControllers() 358 359 } 360 c.main.ShowStandard("Ajout du participant en cours...", true) 361 c.main.Background.Run(job, onSuccess) 362 } 363 364 // CreeParticipantsimple ajoute un participant au camp donné. 365 func (c *Camps) CreeParticipantsimple(participant rd.Participantsimple) { 366 if c.Base.IsParticipantAlreadyHere(participant.IdPersonne, participant.IdCamp) { 367 personne, camp := c.Base.Personnes[participant.IdPersonne], c.Base.Camps[participant.IdCamp] 368 c.main.ShowError(fmt.Errorf("%s est déjà présent(e) au camp %s", personne.NomPrenom(), 369 camp.Label())) 370 return 371 } 372 373 participant.DateHeure = rd.Time(time.Now()) 374 375 job := func() (interface{}, error) { 376 var out rd.Participantsimple 377 err := requete(apiserver.UrlParticipantsimples, http.MethodPut, participant, &out) 378 return out, err 379 } 380 onSuccess := func(_out interface{}) { 381 out := _out.(rd.Participantsimple) 382 c.Base.Participantsimples[out.Id] = out 383 c.main.ShowStandard("Participant ajouté avec succès.", false) 384 c.main.ResetAllControllers() 385 386 } 387 c.main.ShowStandard("Ajout du participant en cours...", true) 388 c.main.Background.Run(job, onSuccess) 389 } 390 391 // MoveToInscrits effectue les vérifications, puis demande propose d'envoyer un mail de notification. 392 // La rotation `_attente` -> `_attente_reponse` -> `_campeur` est appliquée. 393 // Si `notifie` vaut `false` ou si le participant n'a pas de dossier, 394 // le rôle `_attente_reponse` est sauté. 395 // La participant peut être placé manuellement en `_attente_reponse` sans envoyer de mail 396 // en modifiant son rôle directement. 397 func (c *Camps) MoveToInscrits(id rd.IId) { 398 idParticipant, ok := id.(rd.IdParticipant) 399 if !ok { 400 c.main.ShowError(errors.New("Fonctionnalité réservé aux séjours normaux.")) 401 return 402 } 403 participant := c.Base.NewParticipant(idParticipant.Int64()) 404 personne, camp := participant.GetPersonne(), participant.GetCamp() 405 406 hints := camp.HintsAttente(personne.Id, true) 407 if hints.Hint() != rd.Inscrit && !c.Onglet.ConfirmeAjoutParticipant(camp, personne.RawData(), hints.Causes()) { 408 c.main.ShowStandard("Passage dans les inscrits annulé.", false) 409 return 410 } 411 412 raw := participant.RawData() 413 fac, HasFac := participant.GetFacture() 414 if !HasFac { 415 raw.ListeAttente = rd.ListeAttente{} 416 c.UpdateParticipant(raw) 417 return 418 } 419 420 var newRole = rd.AttenteReponse 421 info := "Voulez-vous envoyer un message indiquant qu'une place s'est libérée et demandant une réponse ?" 422 if raw.ListeAttente.Statut == rd.AttenteReponse { 423 newRole = rd.Inscrit 424 info = "Voulez-vous envoyer un message confirmant l'inscription ?" 425 } 426 427 keep, notifie := c.Onglet.ConfirmeNotifPlaceLiberee(info) 428 if !keep { 429 c.main.ShowStandard("Passage dans les inscrits annulé.", false) 430 return 431 } 432 if !notifie { // simple modification, équivalente à changer le rôle directement 433 raw.ListeAttente = rd.ListeAttente{} 434 c.UpdateParticipant(raw) 435 return 436 } 437 c.main.Controllers.SuiviDossiers.EnvoiPlaceLiberee(fac, participant.Id, newRole) 438 } 439 440 func (c *Camps) ExportParticipants(bus rd.Bus, triGroupe, showAttente bool) (filepath string) { 441 if c.Etat.CampCurrent == nil { 442 c.main.ShowError(errors.New("Aucun camp actuel !")) 443 return "" 444 } 445 camp := c.Base.NewCamp(c.Etat.CampCurrent.Int64()) 446 inscrits, attente := camp.GetListes(triGroupe, bus, true, false) 447 if !showAttente { 448 attente = nil 449 } 450 if len(inscrits)+len(attente) == 0 { 451 c.main.ShowError(errors.New("Aucun participant à exporter.")) 452 return 453 } 454 filepath, err := table.EnregistreListeParticipants(c.HeaderInscrits, HeadersResponsables, inscrits, attente, 455 true, string(camp.RawData().Nom), LocalFolder) 456 if err != nil { 457 c.main.ShowError(fmt.Errorf("Impossible de générer la liste : %s", err)) 458 return "" 459 } 460 c.main.ShowStandard(fmt.Sprintf("Liste des participants générée dans %s", filepath), false) 461 return filepath 462 } 463 464 // ChangeCamp effectue les vérifications puis change le participant courant de camp, 465 // de manière SYNCHRONE. 466 func (c *Camps) ChangeCamp(idCamp int64) { 467 if c.Etat.CampCurrent == nil || c.Etat.IdParticipant == nil { 468 c.main.ShowError(errors.New("Aucun camp courant.")) 469 return 470 } 471 isSimple := c.Base.Camps[c.Etat.CampCurrent.Int64()].InscriptionSimple 472 if isSimple { 473 c.main.ShowError(errors.New("Le camp courant ne supporte pas cette fonction.")) 474 return 475 } 476 isTargetSimple := c.Base.Camps[idCamp].InscriptionSimple 477 if isTargetSimple { 478 c.main.ShowError(errors.New("Le champ choisi propose une inscription simplifiée.")) 479 return 480 } 481 482 rawPart := c.Base.NewParticipant(c.Etat.IdParticipant.Int64()).RawData() 483 rawPart.IdCamp = idCamp // on passe sur le camp cible 484 if !c.checkParticipant(rawPart) { 485 return 486 } 487 rawPart.DateHeure = rd.Time(time.Now()) 488 c.UpdateParticipant(rawPart) 489 } 490 491 // SelectCamp sélectionne le camp donné 492 func (c *Camps) SelectCamp(camp int64) { 493 c.Etat.CampCurrent = rd.Id(camp) 494 if c.Base.Camps[camp].IsTerminated() { // besoin d'afficher les camps terminés 495 c.Etat.ShowCampsTerminated = true 496 } 497 c.Onglet.GrabFocus() 498 c.Reset() // le camp courant a changé, besoin des listes, etc.. 499 } 500 501 // SelectParticipant sélectionne le participant donné, en changeant le camp courant 502 // si nécessaire. 503 func (c *Camps) SelectParticipant(part dm.Inscrit) { 504 camp := part.GetCamp() 505 c.Etat.CampCurrent = rd.Id(camp.Id) 506 c.Etat.IdParticipant = part.GetId() 507 if camp.RawData().IsTerminated() { // besoin d'afficher les camps terminés 508 c.Etat.ShowCampsTerminated = true 509 } 510 c.Onglet.GrabFocus() 511 // le camp courant a changé, besoin des listes, etc.. 512 c.Reset() 513 // si l'onglet Camp n'a pas encore été utilisé, la liste ne scroll pas sans ce deuxième appel 514 c.Reset() 515 } 516 517 func (c *Camps) ExportSuiviFinancier(camp int64) string { 518 return c.main.Controllers.SuiviCamps.ExportSuiviFinancierParticipants(c.Base.NewCamp(camp)) 519 } 520 521 // NotifieDirecteur envoie un mail (asynchrone) 522 func (c *MainController) NotifieDirecteur(to, html string) { 523 job := func() (interface{}, error) { 524 var out string 525 err := requete(apiserver.UrlMailsSimple, http.MethodPost, apiserver.ParamsSimpleMail{ 526 Html: html, To: to, Subject: "[ACVE] Message privé", 527 }, &out) 528 return out, err 529 } 530 onSuccess := func(_out interface{}) { 531 out := _out.(string) 532 c.ShowStandard(out, false) 533 } 534 c.ShowStandard("Notification du directeur...", true) 535 c.Background.Run(job, onSuccess) 536 } 537 538 type DiagnosticListeAttente struct { 539 Participant dm.AccesParticipant 540 Hint rd.StatutAttente 541 } 542 543 // CheckListeAttentes compare les listes d'attentes 544 // réelles avec celles calculées automatiquement et émet un diagnostic 545 // en camp de différence : 546 // - optionnel, pour les participants déjà en liste principal 547 // - impératif, pour les participants encore en liste d'attente 548 func (c *Camps) CheckListeAttentes() (inscrits, attentes []DiagnosticListeAttente) { 549 _, cache := c.Base.ResoudParticipants() 550 for idParticipant, part := range c.Base.Participants { 551 acPart := c.Base.NewParticipant(idParticipant) 552 hint := acPart.HintsAttente(cache).Hint() 553 actuel := part.ListeAttente.Statut 554 if actuel == rd.AttenteReponse { // situation temporaire, on ne dit rien 555 continue 556 } 557 if hint == actuel { // tout va bien 558 continue 559 } 560 if part.ListeAttente.Raison != "" { 561 // on ignore les participants avec une raison spéciale 562 continue 563 } 564 diag := DiagnosticListeAttente{Participant: acPart, Hint: hint} 565 if actuel == rd.Inscrit { 566 inscrits = append(inscrits, diag) 567 } else { 568 attentes = append(attentes, diag) 569 } 570 } 571 sortDiags := func(l []DiagnosticListeAttente) { 572 sort.Slice(l, func(i, j int) bool { 573 return l[i].Participant.Id < l[j].Participant.Id 574 }) 575 sort.SliceStable(l, func(i, j int) bool { 576 return l[i].Participant.GetCamp().Id < l[j].Participant.GetCamp().Id 577 }) 578 } 579 sortDiags(inscrits) 580 sortDiags(attentes) 581 return 582 } 583 584 // DocumentCamp est soit `DynamicDocument` soit `RegisteredDocument` 585 type DocumentCamp interface { 586 FileName() string 587 isDocCamp() 588 } 589 590 func (DynamicDocument) isDocCamp() {} 591 func (RegisteredDocument) isDocCamp() {} 592 593 type DynamicDocument struct { 594 documents.DynamicDocument 595 } 596 597 type RegisteredDocument rd.Document 598 599 func (doc RegisteredDocument) FileName() string { return doc.NomClient.String() } 600 601 // GetDocuments renvoie les metas de chaque document disponible. 602 // Si plusieurs lettres sont présentes dans la base, elles sont toutes renvoyées. 603 // Renvois systématiquement les pièces jointes additionnelles. 604 // L'attribut `Locked` est ignoré 605 func GetDocuments(ac dm.AccesCamp) (out []DocumentCamp) { 606 lp := documents.NewListeParticipants(ac) 607 lv := documents.NewListeVetements(ac.RawData()) 608 609 if len(lp.Liste) > 0 { 610 out = append(out, DynamicDocument{DynamicDocument: lp}) 611 } 612 if len(lv.Liste) > 0 { 613 out = append(out, DynamicDocument{DynamicDocument: lv}) 614 } 615 for _, doc := range ac.GetRegisteredDocuments(true, true) { 616 out = append(out, RegisteredDocument(doc)) 617 } 618 return out 619 }