github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/server/espaceperso/views.go (about) 1 package espaceperso 2 3 import ( 4 "errors" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/benoitkugler/goACVE/server/core/apiserver" 10 dm "github.com/benoitkugler/goACVE/server/core/datamodel" 11 12 "github.com/benoitkugler/goACVE/server/documents" 13 "github.com/benoitkugler/goACVE/server/shared" 14 15 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 16 17 "github.com/labstack/echo" 18 ) 19 20 type etatFinancier struct { 21 TotalSejours rd.Euros `json:"total_sejours"` 22 TotalAides rd.Euros `json:"total_aides"` 23 TotalPaiements rd.Euros `json:"total_paiements"` 24 TotalRestant rd.Euros `json:"total_restant"` 25 } 26 27 // Accueil renvoie l'appli statique 28 func Accueil(c echo.Context) error { 29 return c.File("server/static/bv/espace_perso.html") 30 } 31 32 // GetMeta charge les données statiques à l'espace perso. 33 func (ct Controller) GetMetas(c echo.Context) error { 34 _, err := ct.decrypteKey(c.Param("key")) 35 if err != nil { 36 return err 37 } 38 p, err := ct.getMeta(c.Request().Host) 39 if err != nil { 40 return err 41 } 42 return c.JSON(200, p) 43 } 44 45 // GetData charge les données nécessaire à l'espace perso. 46 func (ct Controller) GetData(c echo.Context) error { 47 idFacture, err := ct.decrypteKey(c.Param("key")) 48 if err != nil { 49 return err 50 } 51 base, err := ct.loadData(idFacture) 52 if err != nil { 53 return shared.FormatErr("Le chargement des données (depuis la base) a échoué. Veuillez nous excuser pour ce désagrément.", err) 54 } 55 p, err := ct.compileData(c.Request().Host, idFacture, base) 56 if err != nil { 57 return err 58 } 59 return c.JSON(200, p) 60 } 61 62 // GetFinances charge les données financières à l'espace perso. 63 func (ct Controller) GetFinances(c echo.Context) error { 64 idFacture, err := ct.decrypteKey(c.Param("key")) 65 if err != nil { 66 return err 67 } 68 base, err := ct.loadData(idFacture) 69 if err != nil { 70 return shared.FormatErr("Le chargement des données (depuis la base) a échoué. Veuillez nous excuser pour ce désagrément.", err) 71 } 72 p, err := ct.loadFinances(c.Request().Host, idFacture, base) 73 if err != nil { 74 return err 75 } 76 return c.JSON(200, p) 77 } 78 79 func isFicheSanitaireLocked(mailRespo string, mails []string) bool { 80 out := len(mails) > 0 81 for _, mail := range mails { 82 if strings.TrimSpace(strings.ToLower(mail)) == strings.TrimSpace(strings.ToLower(mailRespo)) { 83 out = false 84 break 85 } 86 } 87 return out 88 } 89 90 // LoadJoomeo renvoie les données permettant l'accès à Joomeo 91 func (ct Controller) LoadJoomeo(c echo.Context) error { 92 idFacture, err := ct.decrypteKey(c.Param("key")) 93 if err != nil { 94 return err 95 } 96 out, err := ct.loadJoomeo(idFacture) 97 if err != nil { 98 return err 99 } 100 return c.JSON(200, out) 101 } 102 103 // CreateDocument crée les meta données d'un document 104 // répondant à la contrainte donnée pour la personne donnée, 105 // et upload le contenu du fichier 106 func (ct Controller) CreateDocument(c echo.Context) error { 107 if _, err := ct.decrypteKey(c.Param("key")); err != nil { 108 return err 109 } 110 111 var args InCreateDocument 112 fileName, fileContent, err := documents.ReceiveUploadWithMeta(c, apiserver.DOCUMENT_MAX_SIZE, &args) 113 if err != nil { 114 return shared.FormatErr("Téléversement impossible.", err) 115 } 116 idPersonne, err := shared.DecodeID(ct.Signing, args.IdPersonne, shared.OrPersonne) 117 if err != nil { 118 return shared.FormatErr("L'identifiant du participant est invalide.", err) 119 } 120 121 idContrainte, err := shared.DecodeID(ct.Signing, args.IdContrainte, shared.OrContrainte) 122 if err != nil { 123 return shared.FormatErr("L'identifiant de la catégorie de document est invalide.", err) 124 } 125 126 doc, err := documents.CreeDocumentPersonne(ct.Signing, ct.DB, c.Request().Host, idPersonne, documents.ParamsNewDocument{ 127 IdContrainte: idContrainte, 128 Description: rd.String("Ajouté via l'espace personnel"), 129 FileContent: fileContent, 130 FileName: fileName, 131 }) 132 if err != nil { 133 return err 134 } 135 return c.JSON(200, doc) 136 } 137 138 // CreateAide ajoute une aide (non validée) au participant donné, 139 // et upload un document associé. 140 func (ct Controller) CreateAide(c echo.Context) error { 141 if _, err := ct.decrypteKey(c.Param("key")); err != nil { 142 return err 143 } 144 var inAide ChampsAideEditables 145 fileName, fileContent, err := documents.ReceiveUploadWithMeta(c, apiserver.DOCUMENT_MAX_SIZE, &inAide) 146 if err != nil { 147 return shared.FormatErr("L'aide à ajouter ne semble pas valide.", err) 148 } 149 150 idPart, err := shared.DecodeID(ct.Signing, inAide.IdParticipantCrypted, shared.OrParticipant) 151 if err != nil { 152 return shared.FormatErr("L'identifiant du participant est corrompu.", err) 153 } 154 idStruct, err := shared.DecodeID(ct.Signing, inAide.IdStructureCrypted, shared.OrStructureaide) 155 if err != nil { 156 return shared.FormatErr("L'identifiant de la structure est corrompu.", err) 157 } 158 newAide := rd.Aide{ 159 IdParticipant: idPart, 160 IdStructureaide: idStruct, 161 Valide: false, 162 NbJoursMax: inAide.NbJoursMax, 163 ParJour: inAide.ParJour, 164 Valeur: inAide.Valeur, 165 } 166 tx, err := ct.DB.Begin() 167 if err != nil { 168 return err 169 } 170 171 part, err := rd.SelectParticipant(tx, newAide.IdParticipant) 172 if err != nil { 173 return shared.Rollback(tx, err) 174 } 175 176 camp, err := rd.SelectCamp(tx, part.IdCamp) 177 if err != nil { 178 return shared.Rollback(tx, err) 179 } 180 181 newAide, err = newAide.Insert(tx) 182 if err != nil { 183 return shared.Rollback(tx, err) 184 } 185 186 newDoc := rd.Document{ 187 Description: "Créé via l'espace personnel", 188 DateHeureModif: rd.Time(time.Now()), 189 } 190 newDoc, err = newDoc.Insert(tx) 191 if err != nil { 192 return shared.Rollback(tx, err) 193 } 194 195 link := rd.DocumentAide{ 196 IdAide: newAide.Id, 197 IdDocument: newDoc.Id, 198 } 199 err = rd.InsertManyDocumentAides(tx, link) 200 if err != nil { 201 return shared.Rollback(tx, err) 202 } 203 204 newDoc, err = documents.SaveDocument(tx, newDoc.Id, fileContent, fileName, true) 205 if err != nil { 206 return shared.Rollback(tx, err) 207 } 208 209 base := dm.BaseLocale{ 210 Camps: rd.Camps{camp.Id: camp}, 211 Participants: rd.Participants{part.Id: part}, 212 Aides: rd.Aides{newAide.Id: newAide}, 213 Documents: rd.Documents{newDoc.Id: newDoc}, 214 } 215 base.DocumentAides = map[int64]rd.DocumentAide{newDoc.Id: link} 216 217 outAide, err := ct.shareAide(c.Request().Host, base.NewAide(newAide.Id)) 218 if err != nil { 219 return shared.Rollback(tx, err) 220 } 221 222 // le participant et la structure ont été choisit par le client 223 outAide.IdParticipantCrypted = inAide.IdParticipantCrypted 224 outAide.IdStructureCrypted = inAide.IdStructureCrypted 225 226 if err = tx.Commit(); err != nil { 227 return shared.FormatErr("La validation de l'ajout du document a échoué.", err) 228 } 229 return c.JSON(200, outAide) 230 } 231 232 // UpdateAide met à jour une aide encore non validée. 233 func (ct Controller) UpdateAide(c echo.Context) error { 234 if _, err := ct.decrypteKey(c.Param("key")); err != nil { 235 return err 236 } 237 var inAide Aide 238 if err := c.Bind(&inAide); err != nil { 239 return shared.FormatErr("L'aide à ajouter ne semble pas valide.", err) 240 } 241 idAide, err := shared.DecodeID(ct.Signing, inAide.IdCrypted, shared.OrAide) 242 if err != nil { 243 return shared.FormatErr("L'identifiant de l'aide est corrompu.", err) 244 } 245 tx, err := ct.DB.Begin() 246 if err != nil { 247 return err 248 } 249 250 aide, err := rd.SelectAide(tx, idAide) 251 if err != nil { 252 return shared.Rollback(tx, err) 253 } 254 if aide.Valide { 255 _ = tx.Rollback() 256 return errors.New("Cette aide a déjà été validée: les modifications sont désactivées.") 257 } 258 259 idPart, err := shared.DecodeID(ct.Signing, inAide.IdParticipantCrypted, shared.OrParticipant) 260 if err != nil { 261 return shared.Rollback(tx, err) 262 } 263 idStruct, err := shared.DecodeID(ct.Signing, inAide.IdStructureCrypted, shared.OrStructureaide) 264 if err != nil { 265 return shared.Rollback(tx, err) 266 } 267 268 aide.NbJoursMax = inAide.NbJoursMax 269 aide.Valeur = inAide.Valeur 270 aide.ParJour = inAide.ParJour 271 aide.IdParticipant = idPart 272 aide.IdStructureaide = idStruct 273 aide, err = aide.Update(tx) 274 if err != nil { 275 return shared.Rollback(tx, err) 276 } 277 278 part, err := rd.SelectParticipant(tx, aide.IdParticipant) 279 if err != nil { 280 return shared.Rollback(tx, err) 281 } 282 283 camp, err := rd.SelectCamp(tx, part.IdCamp) 284 if err != nil { 285 return shared.Rollback(tx, err) 286 } 287 288 base := dm.BaseLocale{ 289 Camps: rd.Camps{camp.Id: camp}, 290 Participants: rd.Participants{part.Id: part}, 291 Aides: rd.Aides{aide.Id: aide}, 292 } 293 294 outAide, err := ct.shareAide(c.Request().Host, base.NewAide(aide.Id)) 295 if err != nil { 296 return shared.Rollback(tx, err) 297 } 298 // on ne modifie pas les champs suivants 299 outAide.IdCrypted = inAide.IdCrypted 300 outAide.IdParticipantCrypted = inAide.IdParticipantCrypted 301 outAide.IdStructureCrypted = inAide.IdStructureCrypted 302 outAide.Document = inAide.Document 303 if err = tx.Commit(); err != nil { 304 return shared.FormatErr("La modification de l'aide a échoué.", err) 305 } 306 return c.JSON(200, outAide) 307 } 308 309 // DeleteAide supprime l'aide non validée et le document associé 310 func (ct Controller) DeleteAide(c echo.Context) error { 311 if _, err := ct.decrypteKey(c.Param("key")); err != nil { 312 return err 313 } 314 id, err := shared.DecodeID(ct.Signing, c.QueryParam("id-crypted"), shared.OrAide) 315 if err != nil { 316 return shared.FormatErr("L'aide n'a pu être identifiée.", err) 317 } 318 319 tx, err := ct.DB.Begin() 320 if err != nil { 321 return err 322 } 323 324 aide, err := rd.SelectAide(tx, id) 325 if err != nil { 326 return shared.Rollback(tx, err) 327 } 328 if aide.Valide { 329 _ = tx.Rollback() 330 return errors.New("Cette aide a déjà été validée: les modifications sont désactivées.") 331 } 332 333 // suppression du lien 334 row := tx.QueryRow("DELETE FROM document_aides WHERE id_aide = $1 RETURNING *", id) 335 documentAide, err := rd.ScanDocumentAide(row) 336 if err != nil { 337 return shared.Rollback(tx, err) 338 } 339 340 // suppression du contenu par cascade 341 342 // suppression du document 343 _, err = rd.DeleteDocumentById(tx, documentAide.IdDocument) 344 if err != nil { 345 return shared.Rollback(tx, err) 346 } 347 348 // finalement, suppression de l'aide 349 if _, err = rd.DeleteAideById(tx, aide.Id); err != nil { 350 return shared.Rollback(tx, err) 351 } 352 if err = tx.Commit(); err != nil { 353 return shared.FormatErr("La suppression de l'aide a échoué.", err) 354 } 355 return c.NoContent(200) 356 } 357 358 // UpdateFicheSanitaire met à jour une fiche sanitaire 359 func (ct Controller) UpdateFicheSanitaire(c echo.Context) error { 360 idFacture, err := ct.decrypteKey(c.Param("key")) 361 if err != nil { 362 return err 363 } 364 var params InFicheSanitaire 365 if err = c.Bind(¶ms); err != nil { 366 return shared.FormatErr("La fiche sanitaire est corrompue", err) 367 } 368 369 out, err := ct.updateFicheSanitaire(idFacture, params) 370 if err != nil { 371 return err 372 } 373 return c.JSON(200, out) 374 } 375 376 // TransfertFicheSanitaire envoie un mail de demande de transfert 377 func (ct Controller) TransfertFicheSanitaire(c echo.Context) error { 378 idFacture, err := ct.decrypteKey(c.Param("key")) 379 if err != nil { 380 return err 381 } 382 idCrypted := c.QueryParam("id-crypted") 383 respo, pers, err := ct.loadDataFicheSanitaire(idFacture, idCrypted) 384 if err != nil { 385 return err 386 } 387 mailErrors, err := ct.SendMailPartageFicheSanitaire(c.Request().Host, respo.Mail.String(), pers) 388 if err != nil { 389 return err 390 } 391 return c.JSON(200, mailErrors) 392 } 393 394 // ValideTransfertFicheSanitaire accepte un transfert. 395 func (ct Controller) ValideTransfertFicheSanitaire(c echo.Context) error { 396 target := c.QueryParam("target") 397 if err := ct.validePartageFicheSanitaire(target); err != nil { 398 return shared.RedirectError(c, err) 399 } 400 return shared.RedirectSuccess(c, "Partage de fiche sanitaire", "Fiche sanitaire partagée", 401 "L'accès à la fiche a bien été mis à jour. <br/>"+ 402 "<small class='text-muted'>Vous pouvez actualiser votre espace de suivi pour y accéder.</small>") 403 } 404 405 // UpdateOptionsParticipants met à jour les participants 406 func (ct Controller) UpdateOptionsParticipants(c echo.Context) error { 407 if _, err := ct.decrypteKey(c.Param("key")); err != nil { 408 return err 409 } 410 var args struct { 411 Participants []Participant `json:"participants"` 412 } 413 if err := c.Bind(&args); err != nil { 414 return shared.FormatErr("Les options n'ont pas pu être lues.", err) 415 } 416 if err := ct.updateOptionsParticipants(args.Participants); err != nil { 417 return err 418 } 419 return c.NoContent(200) 420 } 421 422 // DownloadFacture gènère le document et met à jour les messages 423 // (les données devraient être raffraichies) 424 func (ct Controller) DownloadFacture(c echo.Context) error { 425 idFacture, err := ct.decrypteKey(c.Param("key")) 426 if err != nil { 427 return err 428 } 429 s := c.QueryParam("index-destinataire") 430 indexDestinataire, _ := strconv.Atoi(s) // fallback to responsable 431 out, name, err := ct.downloadFacture(idFacture, indexDestinataire) 432 if err != nil { 433 return shared.RedirectError(c, err) 434 } 435 return documents.Attachment(c, name, out, true) 436 } 437 438 // DownloadAttestationPresence gènère le document et met à jour les messages 439 // (les données devraient être raffraichies) 440 func (ct Controller) DownloadAttestationPresence(c echo.Context) error { 441 idFacture, err := ct.decrypteKey(c.Param("key")) 442 if err != nil { 443 return err 444 } 445 s := c.QueryParam("index-destinataire") 446 indexDestinataire, _ := strconv.Atoi(s) // fallback to responsable 447 out, err := ct.downloadAttestationPresence(idFacture, indexDestinataire) 448 if err != nil { 449 return shared.RedirectError(c, err) 450 } 451 return documents.Attachment(c, "Attestation.pdf", out, true) 452 } 453 454 // MarkConnection enregistre la connection sur l'espace de suivi, 455 // pour permettre la mise à jour de l'état des notifications. 456 func (ct Controller) MarkConnection(c echo.Context) error { 457 idFacture, err := ct.decrypteKey(c.Param("key")) 458 if err != nil { 459 return err 460 } 461 err = ct.markConnection(idFacture) 462 if err != nil { 463 return err 464 } 465 return c.NoContent(200) 466 } 467 468 // SaveSondage enregiste le retour sur un séjour. 469 // Un idCrypted vide crée un nouvel item 470 func (ct Controller) SaveSondage(c echo.Context) error { 471 idFacture, err := ct.decrypteKey(c.Param("key")) 472 if err != nil { 473 return err 474 } 475 var params PublicSondage 476 if err = c.Bind(¶ms); err != nil { 477 return err 478 } 479 out, err := ct.saveSondage(c.Request().Host, idFacture, params) 480 if err != nil { 481 return err 482 } 483 return c.JSON(200, out) 484 } 485 486 // CreeMessage envoie un nouveau message. 487 func (ct Controller) CreeMessage(c echo.Context) error { 488 idFacture, err := ct.decrypteKey(c.Param("key")) 489 if err != nil { 490 return err 491 } 492 var params EditMessage 493 if err = c.Bind(¶ms); err != nil { 494 return err 495 } 496 out, err := ct.creeMessage(idFacture, params.Contenu) 497 if err != nil { 498 return err 499 } 500 return c.JSON(200, out) 501 } 502 503 // EditMessage modifie le contenu d'un message existant. 504 func (ct Controller) EditMessage(c echo.Context) error { 505 idFacture, err := ct.decrypteKey(c.Param("key")) 506 if err != nil { 507 return err 508 } 509 var params EditMessage 510 if err = c.Bind(¶ms); err != nil { 511 return err 512 } 513 out, err := ct.editMessage(idFacture, params) 514 if err != nil { 515 return err 516 } 517 return c.JSON(200, out) 518 } 519 520 // DeleteMessage supprime un message libre. 521 // Le message est marqué comme supprimé, et son contenu est réellement supprimé. 522 func (ct Controller) DeleteMessage(c echo.Context) error { 523 idFacture, err := ct.decrypteKey(c.Param("key")) 524 if err != nil { 525 return err 526 } 527 idMessage, err := shared.ParseId(c, "id-message") 528 if err != nil { 529 return err 530 } 531 out, err := ct.deleteMessage(idFacture, idMessage) 532 if err != nil { 533 return err 534 } 535 return c.JSON(200, out) 536 } 537 538 // ConfirmePlaceliberee passe le participant en liste principale 539 // et recharge les données. 540 func (ct Controller) ConfirmePlaceliberee(c echo.Context) error { 541 idFacture, err := ct.decrypteKey(c.Param("key")) 542 if err != nil { 543 return err 544 } 545 idMessage, err := shared.ParseId(c, "id-message") 546 if err != nil { 547 return err 548 } 549 err = ct.confirmePlaceliberee(idFacture, idMessage) 550 if err != nil { 551 return err 552 } 553 // les participants ont changés 554 base, err := ct.loadData(idFacture) 555 if err != nil { 556 return shared.FormatErr("Le chargement des données (depuis la base) a échoué. Veuillez nous excuser pour ce désagrément.", err) 557 } 558 p, err := ct.compileData(c.Request().Host, idFacture, base) 559 if err != nil { 560 return err 561 } 562 return c.JSON(200, p) 563 }