github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/server/directeurs/contraintes.go (about) 1 package directeurs 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/benoitkugler/goACVE/server/core/datamodel" 10 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 11 "github.com/benoitkugler/goACVE/server/documents" 12 "github.com/benoitkugler/goACVE/server/shared" 13 "github.com/lib/pq" 14 ) 15 16 func (rc DriverCampComplet) loadContraintes(host string) (map[int64]ContrainteComplete, error) { 17 cts, err := rc.fetchContraintes() 18 if err != nil { 19 return nil, err 20 } 21 var idsDocs rd.Ids 22 for _, contrainte := range cts { 23 if idDoc := contrainte.IdDocument; idDoc.IsNotNil() { 24 idsDocs = append(idsDocs, idDoc.Int64) 25 } 26 } 27 // on ajoute les documents 28 rows, err := rc.DB.Query("SELECT * FROM documents WHERE id = ANY($1)", idsDocs.AsSQL()) 29 if err != nil { 30 return nil, err 31 } 32 docs, err := rd.ScanDocuments(rows) 33 if err != nil { 34 return nil, err 35 } 36 out := make(map[int64]ContrainteComplete, len(cts)) 37 for id, contrainte := range cts { 38 ctC := ContrainteComplete{Contrainte: contrainte} 39 if idDoc := contrainte.IdDocument; idDoc.IsNotNil() { 40 ctC.Document, err = documents.PublieDocument(rc.Signing, host, docs[idDoc.Int64]) 41 if err != nil { 42 return nil, err 43 } 44 } 45 out[id] = ctC 46 } 47 return out, nil 48 } 49 50 // renvoie les contraintes builtin et les contraintes perso, pour les participants, sans leurs documents 51 func (rc DriverCampComplet) fetchContraintes() (rd.Contraintes, error) { 52 // on charge l'équipe pour trouver le directeur 53 if err := rc.loadDataEquipiers(); err != nil { 54 return nil, err 55 } 56 builtins := []string{string(rd.CTestNautique)} 57 rows, err := rc.DB.Query("SELECT * FROM contraintes WHERE builtin = ANY($1)", pq.StringArray(builtins)) 58 if err != nil { 59 return nil, err 60 } 61 contraintes, err := rd.ScanContraintes(rows) 62 if err != nil { 63 return nil, err 64 } 65 66 dir, has := rc.camp.GetDirecteur() 67 if !has { 68 return contraintes, err 69 } 70 // on ajoute les contraintes perso 71 contraintesPerso, err := rd.SelectContraintesByIdPersonnes(rc.DB, dir.Id) 72 for k, v := range contraintesPerso { // fusion 73 contraintes[k] = v 74 } 75 return contraintes, err 76 77 } 78 79 func (rc DriverCampComplet) createContrainte(contrainte rd.Contrainte) error { 80 // on charge l'équipe pour trouver le directeur 81 if err := rc.loadDataEquipiers(); err != nil { 82 return err 83 } 84 dir, has := rc.camp.GetDirecteur() 85 if !has { 86 return fmt.Errorf("Un directeur est requis pour ajouter une demande de document personnalisée.") 87 } 88 // on attache la contrainte au directeur 89 contrainte.IdPersonne = rd.NewOptionnalId(dir.Id) 90 _, err := contrainte.Insert(rc.DB) 91 return err 92 } 93 94 func (rc DriverCampComplet) updateContrainte(modif rd.Contrainte) (rd.Contrainte, error) { 95 // on charge l'équipe pour trouver le directeur 96 if err := rc.loadDataEquipiers(); err != nil { 97 return modif, err 98 } 99 dir, has := rc.camp.GetDirecteur() 100 if !has { 101 return modif, fmt.Errorf("Un directeur est requis pour modifier une demande de document personnalisée.") 102 } 103 contrainte, err := rd.SelectContrainte(rc.DB, modif.Id) 104 if err != nil { 105 return modif, err 106 } 107 if contrainte.Builtin != "" { 108 return modif, fmt.Errorf("Cette contrainte est prédéfinie.") 109 } 110 if !contrainte.IdPersonne.Is(dir.Id) { 111 return contrainte, errors.New("Cette contrainte ne vous appartient pas.") 112 } 113 contrainte.Nom = modif.Nom 114 contrainte.Description = modif.Description 115 contrainte.MaxDocs = modif.MaxDocs 116 return contrainte.Update(rc.DB) 117 } 118 119 // crée les métas données d'un document à remplir 120 func (rc DriverCampComplet) lieDocument(host string, idContrainte int64) (documents.PublicDocument, error) { 121 // on charge l'équipe pour trouver le directeur 122 if err := rc.loadDataEquipiers(); err != nil { 123 return documents.PublicDocument{}, err 124 } 125 dir, has := rc.camp.GetDirecteur() 126 if !has { 127 return documents.PublicDocument{}, fmt.Errorf("Un directeur est requis pour modifier une demande de document personnalisée.") 128 } 129 130 tx, err := rc.DB.Begin() 131 if err != nil { 132 return documents.PublicDocument{}, err 133 } 134 135 contrainte, err := rd.SelectContrainte(tx, idContrainte) 136 if err != nil { 137 return documents.PublicDocument{}, shared.Rollback(tx, err) 138 } 139 if contrainte.IdDocument.IsNotNil() { 140 err = errors.New("Un document est déjà lié à cette contrainte.") 141 return documents.PublicDocument{}, shared.Rollback(tx, err) 142 } 143 if !contrainte.IdPersonne.Is(dir.Id) { 144 err = errors.New("Cette contrainte ne vous appartient pas.") 145 return documents.PublicDocument{}, shared.Rollback(tx, err) 146 } 147 148 document := rd.Document{Description: "Document à remplir", DateHeureModif: rd.Time(time.Now())} 149 document, err = document.Insert(tx) 150 if err != nil { 151 return documents.PublicDocument{}, shared.Rollback(tx, err) 152 } 153 154 contrainte.IdDocument = rd.NewOptionnalId(document.Id) 155 contrainte, err = contrainte.Update(tx) 156 if err != nil { 157 return documents.PublicDocument{}, shared.Rollback(tx, err) 158 } 159 160 public, err := documents.PublieDocument(rc.Signing, host, document) 161 if err != nil { 162 return documents.PublicDocument{}, shared.Rollback(tx, err) 163 } 164 err = tx.Commit() 165 return public, err 166 } 167 168 // supprime une contrainte personnalisée et tous les documents associés 169 // et retourne le nombre de documents concernés 170 func (rc DriverCampComplet) deleteContrainte(id int64) (int, error) { 171 // on vérifie le propriétaire 172 if err := rc.loadDataEquipiers(); err != nil { 173 return 0, err 174 } 175 dir, has := rc.camp.GetDirecteur() 176 if !has { // entre autre la contrainte ne peut appartenir au directeur 177 return 0, errors.New("La demande de document ne vous appartient pas.") 178 } 179 contrainte, err := rd.SelectContrainte(rc.DB, id) 180 if err != nil { 181 return 0, err 182 } 183 if !contrainte.IdPersonne.Is(dir.Id) || contrainte.Builtin != "" { // bizarre mais on préfère être safe 184 return 0, errors.New("La demande de document ne vous appartient pas.") 185 } 186 187 tx, err := rc.DB.Begin() 188 if err != nil { 189 return 0, err 190 } 191 192 // lien groupes 193 _, err = rd.DeleteGroupeContraintesByIdContraintes(tx, id) 194 if err != nil { 195 return 0, shared.Rollback(tx, err) 196 } 197 198 // lien documents 199 rows, err := tx.Query("DELETE FROM document_personnes WHERE id_contrainte = $1 RETURNING id_document", id) 200 if err != nil { 201 return 0, shared.Rollback(tx, err) 202 } 203 docIds, err := rd.ScanIds(rows) 204 if err != nil { 205 return 0, shared.Rollback(tx, err) 206 } 207 208 // contenu 209 _, err = rd.DeleteContenuDocumentsByIdDocuments(tx, docIds...) 210 if err != nil { 211 return 0, shared.Rollback(tx, err) 212 } 213 214 // document eux mêmes 215 _, err = rd.DeleteDocumentsByIds(tx, docIds...) 216 if err != nil { 217 return 0, shared.Rollback(tx, err) 218 } 219 220 // finalement la contrainte elle-même 221 _, err = rd.DeleteContrainteById(tx, contrainte.Id) 222 if err != nil { 223 return 0, shared.Rollback(tx, err) 224 } 225 err = tx.Commit() 226 return len(docIds), err 227 } 228 229 // remplace les contraintes actuelles des groupes du camp par celles données 230 func (rc DriverCampComplet) updateGroupesContraintes(contraintes DemandeContraintesIn) error { 231 groupes, _, _, err := loadGroupes(rc.DB, rc.camp.Id) 232 if err != nil { 233 return err 234 } 235 // on vérifie l'origine des groupes 236 for _, cont := range contraintes.GroupeContraintes { 237 if _, in := groupes[cont.IdGroupe]; !in { 238 return fmt.Errorf("Le groupe %d n'est pas lié au séjour.", cont.IdGroupe) 239 } 240 } 241 for i := range contraintes.CampContraintes { 242 contraintes.CampContraintes[i].IdCamp = rc.camp.Id 243 } 244 245 tx, err := rc.DB.Begin() 246 if err != nil { 247 return err 248 } 249 250 // on supprime les contraintes existantes ... 251 _, err = rd.DeleteGroupeContraintesByIdGroupes(tx, groupes.Ids()...) 252 if err != nil { 253 return shared.Rollback(tx, err) 254 } 255 _, err = rd.DeleteCampContraintesByIdCamps(tx, rc.camp.Id) 256 if err != nil { 257 return shared.Rollback(tx, err) 258 } 259 // ... et on ajoute les nouvelles 260 err = rd.InsertManyGroupeContraintes(tx, contraintes.GroupeContraintes...) 261 if err != nil { 262 return shared.Rollback(tx, err) 263 } 264 err = rd.InsertManyCampContraintes(tx, contraintes.CampContraintes...) 265 if err != nil { 266 return shared.Rollback(tx, err) 267 } 268 err = tx.Commit() 269 return err 270 } 271 272 func resoudContraintes(part datamodel.AccesParticipant) rd.Set { 273 cts := rd.NewSet() 274 // on regroupe les ids des contraintes 275 for _, campContrainte := range part.Base.CampContraintes[part.RawData().IdCamp] { 276 cts.Add(campContrainte.IdContrainte) 277 } 278 groupe, hasGroupe := part.GetGroupe() 279 if hasGroupe { 280 for _, campContrainte := range part.Base.GroupeContraintes[groupe.Id] { 281 cts.Add(campContrainte.IdContrainte) 282 } 283 } 284 return cts 285 } 286 287 // renvoie le nombre de participants (du camp courant) 288 // ayant mis un ligne un document pour la contrainte donnée 289 func (rc DriverCampComplet) previewDocsContrainte(idContrainte int64) PreviewDocumentsParticipantsOut { 290 participants := append(rc.camp.GetInscrits(nil), rc.camp.GetAttente(nil)...) 291 292 cache := rc.camp.Base.ResoudDocumentsPersonnes() 293 var out PreviewDocumentsParticipantsOut 294 for _, part := range participants { 295 cts := resoudContraintes(part) 296 if !cts.Has(idContrainte) { 297 // on ignore les documents non exigés 298 continue 299 } 300 out.NbDemandes++ 301 for _, doc := range part.GetPersonne().GetDocuments(cache) { 302 // on ne garde que les docs concerné 303 if idContrainte == doc.GetContrainte().Id { 304 out.NbRecus++ 305 break 306 } 307 } 308 } 309 return out 310 } 311 312 // renvoie les documents des participants (de tous les groupes) correspondant à la contrainte 313 // ou, si elle est est nulle, à toutes les contraintes appliquées aux groupes 314 func (rc DriverCampComplet) downloadDocsParticipants(idContrainte rd.OptionnalId) (*bytes.Buffer, error) { 315 participants := append(rc.camp.GetInscrits(nil), rc.camp.GetAttente(nil)...) 316 datamodel.TriParticipants(participants, true) 317 318 cache := rc.camp.Base.ResoudDocumentsPersonnes() 319 var idsDocs rd.Ids 320 prefixes := map[int64]string{} 321 for _, part := range participants { 322 cts := resoudContraintes(part) 323 if idContrainte.Valid && !cts.Has(idContrainte.Int64) { 324 // on ignore les documents non exigés 325 continue 326 } 327 personne := part.GetPersonne() 328 for _, doc := range personne.GetDocuments(cache) { 329 docCId := doc.GetContrainte().Id 330 // si la contrainte demandée est non nulle, on ne garde que les docs concerné 331 // si la contrainte est nulle, on renvoie tous les documents exigés 332 if idContrainte.Is(docCId) || (idContrainte.IsNil() && cts.Has(docCId)) { 333 idsDocs = append(idsDocs, doc.Id) 334 prefixes[doc.Id] = (doc.GetContrainte().Nom + " " + personne.RawData().NomPrenom()).String() 335 } 336 } 337 338 } 339 return rc.packageDocs(idsDocs.AsSet().Keys(), prefixes) 340 }